Skip to content

Commit b6f7282

Browse files
authored
Merge pull request #87 from 928PJY/object
support generate snippets for object and array according to schema
2 parents e422bde + 0f62274 commit b6f7282

3 files changed

Lines changed: 150 additions & 46 deletions

File tree

src/languageservice/services/yamlCompletion.ts

Lines changed: 133 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,16 @@ export class YAMLCompletion {
290290
if (index < s.schema.items.length) {
291291
this.addSchemaValueCompletions(s.schema.items[index], collector, separatorAfter, true);
292292
}
293-
} else {
293+
} else if (s.schema.items.type === 'object') {
294+
collector.add({
295+
kind: this.getSuggestionKind(s.schema.items.type),
296+
label: `- (array item)`,
297+
documentation: `Create an item of an array${s.schema.description === undefined ? '' : '(' + s.schema.description + ')'}`,
298+
insertText: `- ${this.getInsertTextForObject(s.schema.items, separatorAfter).insertText.trimLeft()}`,
299+
insertTextFormat: InsertTextFormat.Snippet,
300+
});
301+
}
302+
else {
294303
this.addSchemaValueCompletions(s.schema.items, collector, separatorAfter, true);
295304
}
296305
}
@@ -527,6 +536,99 @@ export class YAMLCompletion {
527536
return this.getInsertTextForPlainText(text + separatorAfter);
528537
}
529538

539+
private getInsertTextForObject(schema: JSONSchema, separatorAfter: string, indent = '\t', insertIndex = 1) {
540+
let insertText = "";
541+
if (!schema.properties) {
542+
insertText = `${indent}\$${insertIndex++}\n`;
543+
return { insertText, insertIndex };
544+
}
545+
546+
Object.keys(schema.properties).forEach((key: string) => {
547+
let propertySchema = schema.properties[key];
548+
let type = Array.isArray(propertySchema.type) ? propertySchema.type[0] : propertySchema.type;
549+
if (!type) {
550+
if (propertySchema.properties) {
551+
type = 'object';
552+
}
553+
if (propertySchema.items) {
554+
type = 'array';
555+
}
556+
}
557+
if (schema.required && schema.required.indexOf(key) > -1) {
558+
switch (type) {
559+
case 'boolean':
560+
case 'string':
561+
case 'number':
562+
case 'integer':
563+
insertText += `${indent}${key}: \$${insertIndex++}\n`
564+
break;
565+
case 'array':
566+
let arrayInsertResult = this.getInsertTextForArray(propertySchema.items, separatorAfter, `${indent}\t`, insertIndex++);
567+
insertIndex = arrayInsertResult.insertIndex;
568+
insertText += `${indent}${key}:\n${indent}\t- ${arrayInsertResult.insertText}\n`;
569+
break;
570+
case 'object':
571+
let objectInsertResult = this.getInsertTextForObject(propertySchema, separatorAfter, `${indent}\t`, insertIndex++);
572+
insertIndex = objectInsertResult.insertIndex;
573+
insertText += `${indent}${key}:\n${objectInsertResult.insertText}\n`;
574+
break;
575+
}
576+
} else if (propertySchema.default !== undefined) {
577+
switch (type) {
578+
case 'boolean':
579+
case 'string':
580+
case 'number':
581+
case 'integer':
582+
insertText += `${indent}${key}: \${${insertIndex++}:${propertySchema.default}}\n`
583+
break;
584+
case 'array':
585+
case 'object':
586+
// TODO: support default value for array object
587+
break;
588+
}
589+
}
590+
});
591+
if (insertText.trim().length === 0) {
592+
insertText = `${indent}\$${insertIndex++}\n`;
593+
}
594+
insertText = insertText.trimRight() + separatorAfter;
595+
return { insertText, insertIndex };
596+
}
597+
598+
private getInsertTextForArray(schema: JSONSchema, separatorAfter: string, indent = '\t', insertIndex = 1) {
599+
let insertText = '';
600+
if (!schema) {
601+
insertText = `\$${insertIndex++}`;
602+
}
603+
let type = Array.isArray(schema.type) ? schema.type[0] : schema.type;
604+
if (!type) {
605+
if (schema.properties) {
606+
type = 'object';
607+
}
608+
if (schema.items) {
609+
type = 'array';
610+
}
611+
}
612+
switch (schema.type) {
613+
case 'boolean':
614+
insertText = `\${${insertIndex++}:false}`;
615+
break;
616+
case 'number':
617+
case 'integer':
618+
insertText = `\${${insertIndex++}:0}`;
619+
break;
620+
case 'string':
621+
insertText = `\${${insertIndex++}:null}`;
622+
break;
623+
case 'object':
624+
let objectInsertResult = this.getInsertTextForObject(schema, separatorAfter, `${indent}\t`, insertIndex++);
625+
insertText = objectInsertResult.insertText.trimLeft();
626+
insertIndex = objectInsertResult.insertIndex;
627+
break;
628+
}
629+
return { insertText, insertIndex };
630+
}
631+
530632
private getInsertTextForProperty(key: string, propertySchema: JSONSchema, addValue: boolean, separatorAfter: string): string {
531633

532634
let propertyText = this.getInsertTextForValue(key, '');
@@ -536,48 +638,44 @@ export class YAMLCompletion {
536638
let resultText = propertyText + ':';
537639

538640
let value;
539-
let nValueProposals = 0;
540641
if (propertySchema) {
541642
if (propertySchema.default !== undefined) {
542643
value = ` \${1:${propertySchema.default}}`
543644
}
645+
else if (propertySchema.properties) {
646+
return `${resultText}\n${this.getInsertTextForObject(propertySchema, separatorAfter).insertText}`;
647+
}
648+
else if (propertySchema.items) {
649+
return `${resultText}\n\t- ${this.getInsertTextForArray(propertySchema.items, separatorAfter).insertText}`;
650+
}
544651
else {
545-
if (nValueProposals === 0) {
546-
var type = Array.isArray(propertySchema.type) ? propertySchema.type[0] : propertySchema.type;
547-
if (!type) {
548-
if (propertySchema.properties) {
549-
type = 'object';
550-
} else if (propertySchema.items) {
551-
type = 'array';
552-
}
553-
}
554-
switch (type) {
555-
case 'boolean':
556-
value = ' $1';
557-
break;
558-
case 'string':
559-
value = ' $1';
560-
break;
561-
case 'object':
562-
value = '\n\t';
563-
break;
564-
case 'array':
565-
value = '\n\t- ';
566-
break;
567-
case 'number':
568-
case 'integer':
569-
value = ' ${1:0}';
570-
break;
571-
case 'null':
572-
value = ' ${1:null}';
573-
break;
574-
default:
575-
return propertyText;
576-
}
652+
var type = Array.isArray(propertySchema.type) ? propertySchema.type[0] : propertySchema.type;
653+
switch (type) {
654+
case 'boolean':
655+
value = ' $1';
656+
break;
657+
case 'string':
658+
value = ' $1';
659+
break;
660+
case 'object':
661+
value = '\n\t';
662+
break;
663+
case 'array':
664+
value = '\n\t- ';
665+
break;
666+
case 'number':
667+
case 'integer':
668+
value = ' ${1:0}';
669+
break;
670+
case 'null':
671+
value = ' ${1:null}';
672+
break;
673+
default:
674+
return propertyText;
577675
}
578676
}
579677
}
580-
if (!value || nValueProposals > 1) {
678+
if (!value) {
581679
value = '$1';
582680
}
583681
return resultText + value + separatorAfter;

test/autoCompletion2.test.ts

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,6 @@ suite("Auto Completion Tests", () => {
4949

5050
describe('doComplete', function(){
5151

52-
53-
5452
it('Array autocomplete without word', (done) => {
5553
let content = "authors:\n - ";
5654
let completion = parseSetup(content, 14);
@@ -59,6 +57,22 @@ suite("Auto Completion Tests", () => {
5957
}).then(done, done);
6058
});
6159

60+
it('Array autocomplete without word on array symbol', (done) => {
61+
let content = "authors:\n -";
62+
let completion = parseSetup(content, 13);
63+
completion.then(function(result){
64+
assert.notEqual(result.items.length, 0);
65+
}).then(done, done);
66+
});
67+
68+
it('Array autocomplete without word on space before array symbol', (done) => {
69+
let content = "authors:\n - name: test\n "
70+
let completion = parseSetup(content, 24);
71+
completion.then(function(result){
72+
assert.notEqual(result.items.length, 0);
73+
}).then(done, done);
74+
});
75+
6276
it('Array autocomplete with letter', (done) => {
6377
let content = "authors:\n - n";
6478
let completion = parseSetup(content, 14);
@@ -127,14 +141,6 @@ suite("Auto Completion Tests", () => {
127141
}).then(done, done);
128142
});
129143

130-
it('Autocompletion does not complete on wrong spot in array node', (done) => {
131-
let content = "authors:\n - name: test\n "
132-
let completion = parseSetup(content, 24);
133-
completion.then(function(result){
134-
assert.equal(result.items.length, 0);
135-
}).then(done, done);
136-
});
137-
138144
});
139145

140146
});

test/autoCompletion3.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ suite("Auto Completion Tests", () => {
4141
return completionHelper(testTextDocument, testTextDocument.positionAt(position));
4242
}
4343

44-
it('Array of enum autocomplete without word', (done) => {
44+
it('Array of enum autocomplete without word on array symbol', (done) => {
4545
let content = "optionalUnityReferences:\n -";
4646
let completion = parseSetup(content, 29);
4747
completion.then(function(result){

0 commit comments

Comments
 (0)