@@ -37,6 +37,7 @@ import { indexOf, isInComment, isMapContainsEmptyPair } from '../utils/astUtils'
3737import { isModeline } from './modelineUtil' ;
3838import { getSchemaTypeName , isAnyOfAllOfOneOfType , isPrimitiveType } from '../utils/schemaUtils' ;
3939import { YamlNode } from '../jsonASTTypes' ;
40+ import { addIndentationToMultilineString } from '../utils/strings' ;
4041
4142const localize = nls . loadMessageBundle ( ) ;
4243
@@ -62,6 +63,20 @@ interface CompletionsCollector {
6263 getNumberOfProposals ( ) : number ;
6364 result : CompletionList ;
6465 proposed : { [ key : string ] : CompletionItem } ;
66+ context : {
67+ /**
68+ * The content of the line where the completion is happening.
69+ */
70+ lineContent ?: string ;
71+ /**
72+ * `true` if the line has a colon.
73+ */
74+ hasColon ?: boolean ;
75+ /**
76+ * `true` if the line starts with a hyphen.
77+ */
78+ hasHyphen ?: boolean ;
79+ } ;
6580}
6681
6782interface InsertText {
@@ -459,6 +474,7 @@ export class YamlCompletion {
459474 } ,
460475 result,
461476 proposed,
477+ context : { } ,
462478 } ;
463479
464480 if ( this . customTags && this . customTags . length > 0 ) {
@@ -670,6 +686,10 @@ export class YamlCompletion {
670686 }
671687 }
672688
689+ collector . context . lineContent = lineContent ;
690+ collector . context . hasColon = lineContent . indexOf ( ':' ) !== - 1 ;
691+ collector . context . hasHyphen = lineContent . trimStart ( ) . indexOf ( '-' ) === 0 ;
692+
673693 // completion for object keys
674694 if ( node && isMap ( node ) ) {
675695 // don't suggest properties that are already present
@@ -698,7 +718,7 @@ export class YamlCompletion {
698718 collector . add ( {
699719 kind : CompletionItemKind . Property ,
700720 label : currentWord ,
701- insertText : this . getInsertTextForProperty ( currentWord , null , '' ) ,
721+ insertText : this . getInsertTextForProperty ( currentWord , null , '' , collector ) ,
702722 insertTextFormat : InsertTextFormat . Snippet ,
703723 } ) ;
704724 }
@@ -940,6 +960,7 @@ export class YamlCompletion {
940960 key ,
941961 propertySchema ,
942962 separatorAfter ,
963+ collector ,
943964 indentCompensation + this . indentation
944965 ) ;
945966 }
@@ -970,6 +991,7 @@ export class YamlCompletion {
970991 key ,
971992 propertySchema ,
972993 separatorAfter ,
994+ collector ,
973995 indentCompensation + this . indentation
974996 ) ,
975997 insertTextFormat : InsertTextFormat . Snippet ,
@@ -1127,7 +1149,7 @@ export class YamlCompletion {
11271149 index ?: number
11281150 ) : void {
11291151 const schemaType = getSchemaTypeName ( schema ) ;
1130- const insertText = `- ${ this . getInsertTextForObject ( schema , separatorAfter ) . insertText . trimLeft ( ) } ` ;
1152+ const insertText = `- ${ this . getInsertTextForObject ( schema , separatorAfter , collector ) . insertText . trimLeft ( ) } ` ;
11311153 //append insertText to documentation
11321154 const schemaTypeTitle = schemaType ? ' type `' + schemaType + '`' : '' ;
11331155 const schemaDescription = schema . description ? ' (' + schema . description + ')' : '' ;
@@ -1148,6 +1170,7 @@ export class YamlCompletion {
11481170 key : string ,
11491171 propertySchema : JSONSchema ,
11501172 separatorAfter : string ,
1173+ collector : CompletionsCollector ,
11511174 indent = this . indentation
11521175 ) : string {
11531176 const propertyText = this . getInsertTextForValue ( key , '' , 'string' ) ;
@@ -1218,11 +1241,11 @@ export class YamlCompletion {
12181241 nValueProposals += propertySchema . examples . length ;
12191242 }
12201243 if ( propertySchema . properties ) {
1221- return `${ resultText } \n${ this . getInsertTextForObject ( propertySchema , separatorAfter , indent ) . insertText } ` ;
1244+ return `${ resultText } \n${ this . getInsertTextForObject ( propertySchema , separatorAfter , collector , indent ) . insertText } ` ;
12221245 } else if ( propertySchema . items ) {
1223- return ` ${ resultText } \n ${ indent } - ${
1224- this . getInsertTextForArray ( propertySchema . items , separatorAfter , 1 , indent ) . insertText
1225- } ` ;
1246+ let insertText = this . getInsertTextForArray ( propertySchema . items , separatorAfter , collector , 1 , indent ) . insertText ;
1247+ insertText = resultText + addIndentationToMultilineString ( insertText , `\n ${ indent } - ` , ' ' ) ;
1248+ return insertText ;
12261249 }
12271250 if ( nValueProposals === 0 ) {
12281251 switch ( type ) {
@@ -1262,10 +1285,30 @@ export class YamlCompletion {
12621285 private getInsertTextForObject (
12631286 schema : JSONSchema ,
12641287 separatorAfter : string ,
1288+ collector : CompletionsCollector ,
12651289 indent = this . indentation ,
12661290 insertIndex = 1
12671291 ) : InsertText {
12681292 let insertText = '' ;
1293+ if ( Array . isArray ( schema . defaultSnippets ) && schema . defaultSnippets . length === 1 ) {
1294+ const body = schema . defaultSnippets [ 0 ] . body ;
1295+ if ( isDefined ( body ) ) {
1296+ let value = this . getInsertTextForSnippetValue (
1297+ body ,
1298+ '' ,
1299+ {
1300+ newLineFirst : false ,
1301+ indentFirstObject : false ,
1302+ shouldIndentWithTab : false ,
1303+ } ,
1304+ [ ] ,
1305+ 0
1306+ ) ;
1307+ value = addIndentationToMultilineString ( value , indent , indent ) ;
1308+
1309+ return { insertText : value , insertIndex } ;
1310+ }
1311+ }
12691312 if ( ! schema . properties ) {
12701313 insertText = `${ indent } $${ insertIndex ++ } \n` ;
12711314 return { insertText, insertIndex } ;
@@ -1306,25 +1349,30 @@ export class YamlCompletion {
13061349 }
13071350 case 'array' :
13081351 {
1309- const arrayInsertResult = this . getInsertTextForArray ( propertySchema . items , separatorAfter , insertIndex ++ , indent ) ;
1310- const arrayInsertLines = arrayInsertResult . insertText . split ( '\n' ) ;
1311- let arrayTemplate = arrayInsertResult . insertText ;
1312- if ( arrayInsertLines . length > 1 ) {
1313- for ( let index = 1 ; index < arrayInsertLines . length ; index ++ ) {
1314- const element = arrayInsertLines [ index ] ;
1315- arrayInsertLines [ index ] = ` ${ element } ` ;
1316- }
1317- arrayTemplate = arrayInsertLines . join ( '\n' ) ;
1318- }
1352+ const arrayInsertResult = this . getInsertTextForArray (
1353+ propertySchema . items ,
1354+ separatorAfter ,
1355+ collector ,
1356+ insertIndex ++ ,
1357+ indent
1358+ ) ;
1359+
13191360 insertIndex = arrayInsertResult . insertIndex ;
1320- insertText += `${ indent } ${ keyEscaped } :\n${ indent } ${ this . indentation } - ${ arrayTemplate } \n` ;
1361+ insertText +=
1362+ `${ indent } ${ keyEscaped } :` +
1363+ addIndentationToMultilineString (
1364+ arrayInsertResult . insertText ,
1365+ `\n${ indent } ${ this . indentation } - ` ,
1366+ `${ this . indentation } `
1367+ ) ;
13211368 }
13221369 break ;
13231370 case 'object' :
13241371 {
13251372 const objectInsertResult = this . getInsertTextForObject (
13261373 propertySchema ,
13271374 separatorAfter ,
1375+ collector ,
13281376 `${ indent } ${ this . indentation } ` ,
13291377 insertIndex ++
13301378 ) ;
@@ -1360,8 +1408,14 @@ export class YamlCompletion {
13601408 return { insertText, insertIndex } ;
13611409 }
13621410
1363- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1364- private getInsertTextForArray ( schema : any , separatorAfter : string , insertIndex = 1 , indent = this . indentation ) : InsertText {
1411+ private getInsertTextForArray (
1412+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1413+ schema : any ,
1414+ separatorAfter : string ,
1415+ collector : CompletionsCollector ,
1416+ insertIndex = 1 ,
1417+ indent = this . indentation
1418+ ) : InsertText {
13651419 let insertText = '' ;
13661420 if ( ! schema ) {
13671421 insertText = `$${ insertIndex ++ } ` ;
@@ -1389,7 +1443,7 @@ export class YamlCompletion {
13891443 break ;
13901444 case 'object' :
13911445 {
1392- const objectInsertResult = this . getInsertTextForObject ( schema , separatorAfter , ` ${ indent } ` , insertIndex ++ ) ;
1446+ const objectInsertResult = this . getInsertTextForObject ( schema , separatorAfter , collector , indent , insertIndex ++ ) ;
13931447 insertText = objectInsertResult . insertText . trimLeft ( ) ;
13941448 insertIndex = objectInsertResult . insertIndex ;
13951449 }
@@ -1591,11 +1645,11 @@ export class YamlCompletion {
15911645 indentFirstObject : ! isArray ,
15921646 shouldIndentWithTab : ! isArray ,
15931647 } ,
1594- 0 ,
1648+ arrayDepth ,
15951649 isArray
15961650 ) ;
15971651 if ( ! hasProposals && typeof schema . items === 'object' && ! Array . isArray ( schema . items ) ) {
1598- this . addDefaultValueCompletions ( schema . items , separatorAfter , collector , arrayDepth + 1 ) ;
1652+ this . addDefaultValueCompletions ( schema . items , separatorAfter , collector , arrayDepth + 1 , true ) ;
15991653 }
16001654 }
16011655
@@ -1656,24 +1710,13 @@ export class YamlCompletion {
16561710 if ( Array . isArray ( schema . defaultSnippets ) ) {
16571711 for ( const s of schema . defaultSnippets ) {
16581712 let type = schema . type ;
1659- let value = s . body ;
1713+ const value = s . body ;
16601714 let label = s . label ;
16611715 let insertText : string ;
16621716 let filterText : string ;
16631717 if ( isDefined ( value ) ) {
16641718 const type = s . type || schema . type ;
1665- if ( ( arrayDepth === 0 && type === 'array' ) || isArray ) {
1666- // We know that a - isn't present yet so we need to add one
1667- const fixedObj = { } ;
1668- Object . keys ( value ) . forEach ( ( val , index ) => {
1669- if ( index === 0 && ! val . startsWith ( '-' ) ) {
1670- fixedObj [ `- ${ val } ` ] = value [ val ] ;
1671- } else {
1672- fixedObj [ ` ${ val } ` ] = value [ val ] ;
1673- }
1674- } ) ;
1675- value = fixedObj ;
1676- }
1719+
16771720 const existingProps = Object . keys ( collector . proposed ) . filter (
16781721 ( proposedProp ) => collector . proposed [ proposedProp ] . label === existingProposeItem
16791722 ) ;
@@ -1683,6 +1726,24 @@ export class YamlCompletion {
16831726 if ( insertText === '' && value ) {
16841727 continue ;
16851728 }
1729+
1730+ if ( ( arrayDepth === 0 && type === 'array' ) || isArray ) {
1731+ // add extra hyphen if we are in array, but the hyphen is missing on current line
1732+ // but don't add it for array value because it's already there from getInsertTextForSnippetValue
1733+ const addHyphen = ! collector . context . hasHyphen && ! Array . isArray ( value ) ? '- ' : '' ;
1734+ // add new line if the cursor is after the colon
1735+ const addNewLine = collector . context . hasColon ? `\n${ this . indentation } ` : '' ;
1736+ // add extra indent if new line and hyphen are added
1737+ const addIndent = isArray && addNewLine && addHyphen ? this . indentation : '' ;
1738+ // const addIndent = addHyphen && addNewLine ? this.indentation : '';
1739+
1740+ insertText = addIndentationToMultilineString (
1741+ insertText . trimStart ( ) ,
1742+ `${ addNewLine } ${ addHyphen } ` ,
1743+ `${ addIndent } ${ this . indentation } `
1744+ ) ;
1745+ }
1746+
16861747 label = label || this . getLabelForSnippetValue ( value ) ;
16871748 } else if ( typeof s . bodyText === 'string' ) {
16881749 let prefix = '' ,
0 commit comments