Skip to content

Commit

Permalink
intellisense doesn't work for array item (#85)
Browse files Browse the repository at this point in the history
* fix intellisense error for array item
  • Loading branch information
928PJY authored and JPinkney committed Sep 6, 2018
1 parent 90fc8c0 commit e422bde
Show file tree
Hide file tree
Showing 5 changed files with 162 additions and 32 deletions.
68 changes: 39 additions & 29 deletions src/languageservice/services/yamlCompletion.ts
Expand Up @@ -189,9 +189,8 @@ export class YAMLCompletion {
}

// proposals for values
let types: { [type: string]: boolean } = {};
if (newSchema) {
this.getValueCompletions(newSchema, currentDoc, node, offset, document, collector, types);
this.getValueCompletions(newSchema, currentDoc, node, offset, document, collector);
}
if (this.contributions.length > 0) {
this.getContributedValueCompletions(currentDoc, node, offset, document, collector, collectionPromises);
Expand Down Expand Up @@ -225,11 +224,19 @@ export class YAMLCompletion {
}
});
}
}
// Error fix
// If this is a array of string/boolean/number
// test:
// - item1
// it will treated as a property key since `:` has been appended
if (node.type === 'object' && node.parent && node.parent.type === 'array' && s.schema.type !== 'object') {
this.addSchemaValueCompletions(s.schema, collector, separatorAfter)
}
}
});
}

private getValueCompletions(schema: SchemaService.ResolvedSchema, doc, node: Parser.ASTNode, offset: number, document: TextDocument, collector: CompletionsCollector, types: { [type: string]: boolean } ): void {
private getValueCompletions(schema: SchemaService.ResolvedSchema, doc, node: Parser.ASTNode, offset: number, document: TextDocument, collector: CompletionsCollector): void {
let offsetForSeparator = offset;
let parentKey: string = null;
let valueNode: Parser.ASTNode = null;
Expand Down Expand Up @@ -258,7 +265,7 @@ export class YAMLCompletion {
}

if (!node) {
this.addSchemaValueCompletions(schema.schema, collector, types, "");
this.addSchemaValueCompletions(schema.schema, collector, "");
return;
}

Expand All @@ -281,30 +288,21 @@ export class YAMLCompletion {
if (Array.isArray(s.schema.items)) {
let index = this.findItemAtOffset(node, document, offset);
if (index < s.schema.items.length) {
this.addSchemaValueCompletions(s.schema.items[index], collector, types, separatorAfter);
this.addSchemaValueCompletions(s.schema.items[index], collector, separatorAfter, true);
}
} else {
this.addSchemaValueCompletions(s.schema.items, collector, types, separatorAfter);
this.addSchemaValueCompletions(s.schema.items, collector, separatorAfter, true);
}
}
if (s.schema.properties) {
let propertySchema = s.schema.properties[parentKey];
if (propertySchema) {
this.addSchemaValueCompletions(propertySchema, collector, types, separatorAfter);
this.addSchemaValueCompletions(propertySchema, collector, separatorAfter, false);
}
}
}
});
}
if(node){
if (types['boolean']) {
this.addBooleanValueCompletion(true, collector, separatorAfter);
this.addBooleanValueCompletion(false, collector, separatorAfter);
}
if (types['null']) {
this.addNullValueCompletion(collector, separatorAfter);
}
}
}

private getContributedValueCompletions(doc: Parser.JSONDocument, node: Parser.ASTNode, offset: number, document: TextDocument, collector: CompletionsCollector, collectionPromises: Thenable<any>[]) {
Expand Down Expand Up @@ -345,22 +343,34 @@ export class YAMLCompletion {
});
}

private addSchemaValueCompletions(schema: JSONSchema, collector: CompletionsCollector, types: { [type: string]: boolean }, separatorAfter: string): void {
this.addDefaultValueCompletions(schema, collector, separatorAfter);
this.addEnumValueCompletions(schema, collector, separatorAfter);
private addSchemaValueCompletions(schema: JSONSchema, collector: CompletionsCollector, separatorAfter: string, forArrayItem = false): void {
let types: { [type: string]: boolean } = {};
this.addSchemaValueCompletionsCore(schema, collector, types, separatorAfter, forArrayItem);
if (types['boolean']) {
this.addBooleanValueCompletion(true, collector, separatorAfter);
this.addBooleanValueCompletion(false, collector, separatorAfter);
}
if (types['null']) {
this.addNullValueCompletion(collector, separatorAfter);
}
}

private addSchemaValueCompletionsCore(schema: JSONSchema, collector: CompletionsCollector, types: { [type: string]: boolean }, separatorAfter: string, forArrayItem = false): void {
this.addDefaultValueCompletions(schema, collector, separatorAfter, 0, forArrayItem);
this.addEnumValueCompletions(schema, collector, separatorAfter, forArrayItem);
this.collectTypes(schema, types);
if (Array.isArray(schema.allOf)) {
schema.allOf.forEach(s => this.addSchemaValueCompletions(s, collector, types, separatorAfter));
schema.allOf.forEach(s => this.addSchemaValueCompletionsCore(s, collector, types, separatorAfter, forArrayItem));
}
if (Array.isArray(schema.anyOf)) {
schema.anyOf.forEach(s => this.addSchemaValueCompletions(s, collector, types, separatorAfter));
schema.anyOf.forEach(s => this.addSchemaValueCompletionsCore(s, collector, types, separatorAfter, forArrayItem));
}
if (Array.isArray(schema.oneOf)) {
schema.oneOf.forEach(s => this.addSchemaValueCompletions(s, collector, types, separatorAfter));
schema.oneOf.forEach(s => this.addSchemaValueCompletionsCore(s, collector, types, separatorAfter, forArrayItem));
}
}

private addDefaultValueCompletions(schema: JSONSchema, collector: CompletionsCollector, separatorAfter: string, arrayDepth = 0): void {
private addDefaultValueCompletions(schema: JSONSchema, collector: CompletionsCollector, separatorAfter: string, arrayDepth = 0, forArrayItem = false): void {
let hasProposals = false;
if (schema.default) {
let type = schema.type;
Expand All @@ -371,8 +381,8 @@ export class YAMLCompletion {
}
collector.add({
kind: this.getSuggestionKind(type),
label: this.getLabelForValue(value),
insertText: this.getInsertTextForValue(value, separatorAfter),
label: forArrayItem ? `- ${this.getLabelForValue(value)}` : this.getLabelForValue(value),
insertText: forArrayItem ? `- ${this.getInsertTextForValue(value, separatorAfter)}` : this.getInsertTextForValue(value, separatorAfter),
insertTextFormat: InsertTextFormat.Snippet,
detail: localize('json.suggest.default', 'Default value'),
});
Expand All @@ -383,7 +393,7 @@ export class YAMLCompletion {
}
}

private addEnumValueCompletions(schema: JSONSchema, collector: CompletionsCollector, separatorAfter: string): void {
private addEnumValueCompletions(schema: JSONSchema, collector: CompletionsCollector, separatorAfter: string, forArrayItem = false): void {
if (Array.isArray(schema.enum)) {
for (let i = 0, length = schema.enum.length; i < length; i++) {
let enm = schema.enum[i];
Expand All @@ -393,8 +403,8 @@ export class YAMLCompletion {
}
collector.add({
kind: this.getSuggestionKind(schema.type),
label: this.getLabelForValue(enm),
insertText: this.getInsertTextForValue(enm, separatorAfter),
label: forArrayItem ? `- ${this.getLabelForValue(enm)}` : this.getLabelForValue(enm),
insertText: forArrayItem ? `- ${this.getInsertTextForValue(enm, separatorAfter)}` : this.getInsertTextForValue(enm, separatorAfter),
insertTextFormat: InsertTextFormat.Snippet,
documentation
});
Expand Down
2 changes: 1 addition & 1 deletion src/server.ts
Expand Up @@ -494,7 +494,7 @@ function completionHelper(document: TextDocument, textDocumentPosition: Position
let trimmedText = textLine.trim();
if(trimmedText.length === 0 || (trimmedText.length === 1 && trimmedText[0] === '-')){
//Add a temp node that is in the document but we don't use at all.
newText = document.getText().substring(0, start+textLine.length) + "holder:\r\n" + document.getText().substr(lineOffset[linePos+1] || document.getText().length);
newText = document.getText().substring(0, start + textLine.length) + (trimmedText[0] === '-' && !textLine.endsWith(" ") ? " " : "") + "holder:\r\n" + document.getText().substr(lineOffset[linePos + 1] || document.getText().length);

//For when missing semi colon case
}else{
Expand Down
2 changes: 1 addition & 1 deletion test/autoCompletion.test.ts
Expand Up @@ -199,7 +199,7 @@ function completionHelper(document: TextDocument, textDocumentPosition){
let trimmedText = textLine.trim();
if(trimmedText.length === 0 || (trimmedText.length === 1 && trimmedText[0] === '-')){
//Add a temp node that is in the document but we don't use at all.
newText = document.getText().substring(0, start+textLine.length) + "holder:\r\n" + document.getText().substr(lineOffset[linePos+1] || document.getText().length);
newText = document.getText().substring(0, start + textLine.length) + (trimmedText[0] === '-' && !textLine.endsWith(" ") ? " " : "") + "holder:\r\n" + document.getText().substr(lineOffset[linePos + 1] || document.getText().length);
//For when missing semi colon case
}else{
//Add a semicolon to the end of the current line so we can validate the node
Expand Down
2 changes: 1 addition & 1 deletion test/autoCompletion2.test.ts
Expand Up @@ -175,7 +175,7 @@ function completionHelper(document: TextDocument, textDocumentPosition){
let trimmedText = textLine.trim();
if(trimmedText.length === 0 || (trimmedText.length === 1 && trimmedText[0] === '-')){
//Add a temp node that is in the document but we don't use at all.
newText = document.getText().substring(0, start+textLine.length) + "holder:\r\n" + document.getText().substr(lineOffset[linePos+1] || document.getText().length);
newText = document.getText().substring(0, start + textLine.length) + (trimmedText[0] === '-' && !textLine.endsWith(" ") ? " " : "") + "holder:\r\n" + document.getText().substr(lineOffset[linePos + 1] || document.getText().length);
//For when missing semi colon case
}else{
//Add a semicolon to the end of the current line so we can validate the node
Expand Down
120 changes: 120 additions & 0 deletions test/autoCompletion3.test.ts
@@ -0,0 +1,120 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Red Hat. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import {
IPCMessageReader, IPCMessageWriter,
createConnection, IConnection, TextDocumentSyncKind,
TextDocuments, TextDocument, Diagnostic, DiagnosticSeverity,
InitializeParams, InitializeResult, TextDocumentPositionParams,
CompletionItem, CompletionItemKind, RequestType
} from 'vscode-languageserver';
import {getLanguageService} from '../src/languageservice/yamlLanguageService'
import {JSONSchemaService} from '../src/languageservice/services/jsonSchemaService'
import {schemaRequestService, workspaceContext} from './testHelper';
import { parse as parseYAML } from '../src/languageservice/parser/yamlParser';
import { getLineOffsets } from '../src/languageservice/utils/arrUtils';
var assert = require('assert');

let languageService = getLanguageService(schemaRequestService, workspaceContext, [], null);

let uri = 'http://json.schemastore.org/asmdef';
let languageSettings = {
schemas: []
};
let fileMatch = ["*.yml", "*.yaml"];
languageSettings.schemas.push({ uri, fileMatch: fileMatch });
languageService.configure(languageSettings);

suite("Auto Completion Tests", () => {

describe('yamlCompletion with asmdef', function(){

describe('doComplete', function(){

function setup(content: string){
return TextDocument.create("file://~/Desktop/vscode-k8s/test.yaml", "yaml", 0, content);
}

function parseSetup(content: string, position){
let testTextDocument = setup(content);
return completionHelper(testTextDocument, testTextDocument.positionAt(position));
}

it('Array of enum autocomplete without word', (done) => {
let content = "optionalUnityReferences:\n -";
let completion = parseSetup(content, 29);
completion.then(function(result){
assert.notEqual(result.items.length, 0);
}).then(done, done);
});

it('Array of enum autocomplete without word', (done) => {
let content = "optionalUnityReferences:\n - ";
let completion = parseSetup(content, 30);
completion.then(function(result){
assert.notEqual(result.items.length, 0);
}).then(done, done);
});

it('Array of enum autocomplete with letter', (done) => {
let content = "optionalUnityReferences:\n - T";
let completion = parseSetup(content, 31);
completion.then(function(result){
assert.notEqual(result.items.length, 0);
}).then(done, done);
});
});
});
});

function is_EOL(c) {
return (c === 0x0A/* LF */) || (c === 0x0D/* CR */);
}

function completionHelper(document: TextDocument, textDocumentPosition){

//Get the string we are looking at via a substring
let linePos = textDocumentPosition.line;
let position = textDocumentPosition;
let lineOffset = getLineOffsets(document.getText());
let start = lineOffset[linePos]; //Start of where the autocompletion is happening
let end = 0; //End of where the autocompletion is happening
if(lineOffset[linePos+1]){
end = lineOffset[linePos+1];
}else{
end = document.getText().length;
}

while (end - 1 >= 0 && is_EOL(document.getText().charCodeAt(end - 1))) {
end--;
}

let textLine = document.getText().substring(start, end);

//Check if the string we are looking at is a node
if(textLine.indexOf(":") === -1){
//We need to add the ":" to load the nodes

let newText = "";

//This is for the empty line case
let trimmedText = textLine.trim();
if(trimmedText.length === 0 || (trimmedText.length === 1 && trimmedText[0] === '-')){
//Add a temp node that is in the document but we don't use at all.
newText = document.getText().substring(0, start + textLine.length) + (trimmedText[0] === '-' && !textLine.endsWith(" ") ? " " : "") + "holder:\r\n" + document.getText().substr(lineOffset[linePos + 1] || document.getText().length);
//For when missing semi colon case
}else{
//Add a semicolon to the end of the current line so we can validate the node
newText = document.getText().substring(0, start+textLine.length) + ":\r\n" + document.getText().substr(lineOffset[linePos+1] || document.getText().length);
}
let jsonDocument = parseYAML(newText);
return languageService.doComplete(document, position, jsonDocument);
}else{

//All the nodes are loaded
position.character = position.character - 1;
let jsonDocument = parseYAML(document.getText());
return languageService.doComplete(document, position, jsonDocument);
}
}

0 comments on commit e422bde

Please sign in to comment.