diff --git a/src/server.ts b/src/server.ts index dce11a4a..968c4f8f 100755 --- a/src/server.ts +++ b/src/server.ts @@ -450,60 +450,62 @@ connection.onCompletion(textDocumentPosition => { return customLanguageService.doComplete(textDocument, textDocumentPosition.position, jsonDocument); }); +function is_EOL(c) { + return (c === 0x0A/* LF */) || (c === 0x0D/* CR */); +} + function completionHelper(document: TextDocument, textDocumentPosition: Position){ - //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]; + //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) + "holder:\r\n" + document.getText().substr(lineOffset[linePos+1] || document.getText().length); + + //For when missing semi colon case }else{ - end = document.getText().length; + //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 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. - if(lineOffset[linePos+1]){ - newText = document.getText().substring(0, start+(textLine.length-1)) + "holder:\r\n" + document.getText().substr(end+2); - }else{ - newText = document.getText().substring(0, start+(textLine.length)) + "holder:\r\n" + document.getText().substr(end+2); - } - //For when missing semi colon case - }else{ - //Add a semicolon to the end of the current line so we can validate the node - if(lineOffset[linePos+1]){ - newText = document.getText().substring(0, start+(textLine.length-1)) + ":\r\n" + document.getText().substr(end+2); - }else{ - newText = document.getText().substring(0, start+(textLine.length)) + ":\r\n" + document.getText().substr(end+2); - } - } - return { - "newText": newText, - "newPosition": textDocumentPosition - } + return { + "newText": newText, + "newPosition": textDocumentPosition + } - }else{ + }else{ - //All the nodes are loaded - position.character = position.character - 1; - return { - "newText": document.getText(), - "newPosition": position - } + //All the nodes are loaded + position.character = position.character - 1; + return { + "newText": document.getText(), + "newPosition": position } + } } diff --git a/test/autoCompletion.test.ts b/test/autoCompletion.test.ts index e1c6dd8b..3e96dbc9 100644 --- a/test/autoCompletion.test.ts +++ b/test/autoCompletion.test.ts @@ -36,11 +36,11 @@ languageService.configure(languageSettings); suite("Auto Completion Tests", () => { - + describe('yamlCompletion with bowerrc', function(){ - + describe('doComplete', function(){ - + function setup(content: string){ return TextDocument.create("file://~/Desktop/vscode-k8s/test.yaml", "yaml", 0, content); } @@ -55,7 +55,7 @@ suite("Auto Completion Tests", () => { let content = ""; let completion = parseSetup(content, 0); completion.then(function(result){ - assert.notEqual(result.items.length, 0); + assert.notEqual(result.items.length, 0); }).then(done, done); }); @@ -123,7 +123,7 @@ suite("Auto Completion Tests", () => { }).then(done, done); }); - it('Autocomplete key in middle of file 2', (done) => { + it('Autocomplete key in middle of file 2', (done) => { let content = "scripts:\n postinstall: /test\n preinsta"; let completion = parseSetup(content, 31); completion.then(function(result){ @@ -131,7 +131,7 @@ suite("Auto Completion Tests", () => { }).then(done, done); }); - it('Autocomplete does not happen right after :', (done) => { + it('Autocomplete does not happen right after :', (done) => { let content = "analytics:"; let completion = parseSetup(content, 9); completion.then(function(result){ @@ -139,7 +139,7 @@ suite("Auto Completion Tests", () => { }).then(done, done); }); - it('Autocomplete does not happen right after : under an object', (done) => { + it('Autocomplete does not happen right after : under an object', (done) => { let content = "scripts:\n postinstall:"; let completion = parseSetup(content, 21); completion.then(function(result){ @@ -147,7 +147,7 @@ suite("Auto Completion Tests", () => { }).then(done, done); }); - it('Autocomplete on multi yaml documents in a single file on root', (done) => { + it('Autocomplete on multi yaml documents in a single file on root', (done) => { let content = `---\nanalytics: true\n...\n---\n...`; let completion = parseSetup(content, 28); completion.then(function(result){ @@ -155,7 +155,7 @@ suite("Auto Completion Tests", () => { }).then(done, done); }); - it('Autocomplete on multi yaml documents in a single file on scalar', (done) => { + it('Autocomplete on multi yaml documents in a single file on scalar', (done) => { let content = `---\nanalytics: true\n...\n---\njson: \n...`; let completion = parseSetup(content, 34); completion.then(function(result){ @@ -166,55 +166,54 @@ suite("Auto Completion Tests", () => { }); }); +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; - } - 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. - if(lineOffset[linePos+1]){ - newText = document.getText().substring(0, start+(textLine.length-1)) + "holder:\r\n" + document.getText().substr(end+2); - }else{ - newText = document.getText().substring(0, start+(textLine.length)) + "holder:\r\n" + document.getText().substr(end+2); - } - + + //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) + "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 - if(lineOffset[linePos+1]){ - newText = document.getText().substring(0, start+(textLine.length-1)) + ":\r\n" + document.getText().substr(end+2); - }else{ - newText = document.getText().substring(0, start+(textLine.length)) + ":\r\n" + document.getText().substr(end+2); - } - } - 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); + //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); + } } \ No newline at end of file diff --git a/test/autoCompletion2.test.ts b/test/autoCompletion2.test.ts index d6a07ffe..8f3ee623 100644 --- a/test/autoCompletion2.test.ts +++ b/test/autoCompletion2.test.ts @@ -47,20 +47,20 @@ suite("Auto Completion Tests", () => { } describe('yamlCompletion with composer', function(){ - + describe('doComplete', function(){ - - - it('Array autocomplete without word', (done) => { + + + it('Array autocomplete without word', (done) => { let content = "authors:\n - "; let completion = parseSetup(content, 14); completion.then(function(result){ assert.notEqual(result.items.length, 0); }).then(done, done); }); - - it('Array autocomplete with letter', (done) => { + + it('Array autocomplete with letter', (done) => { let content = "authors:\n - n"; let completion = parseSetup(content, 14); completion.then(function(result){ @@ -68,7 +68,7 @@ suite("Auto Completion Tests", () => { }).then(done, done); }); - it('Array autocomplete without word (second item)', (done) => { + it('Array autocomplete without word (second item)', (done) => { let content = "authors:\n - name: test\n "; let completion = parseSetup(content, 32); completion.then(function(result){ @@ -76,7 +76,7 @@ suite("Auto Completion Tests", () => { }).then(done, done); }); - it('Array autocomplete with letter (second item)', (done) => { + it('Array autocomplete with letter (second item)', (done) => { let content = "authors:\n - name: test\n e"; let completion = parseSetup(content, 27); completion.then(function(result){ @@ -84,7 +84,7 @@ suite("Auto Completion Tests", () => { }).then(done, done); }); - it('Autocompletion after array', (done) => { + it('Autocompletion after array', (done) => { let content = "authors:\n - name: test\n" let completion = parseSetup(content, 24); completion.then(function(result){ @@ -92,7 +92,7 @@ suite("Auto Completion Tests", () => { }).then(done, done); }); - it('Autocompletion after array with depth', (done) => { + it('Autocompletion after array with depth', (done) => { let content = "archive:\n exclude:\n - test\n" let completion = parseSetup(content, 29); completion.then(function(result){ @@ -100,7 +100,7 @@ suite("Auto Completion Tests", () => { }).then(done, done); }); - it('Autocompletion after array with depth', (done) => { + it('Autocompletion after array with depth', (done) => { let content = "autoload:\n classmap:\n - test\n exclude-from-classmap:\n - test\n " let completion = parseSetup(content, 70); completion.then(function(result){ @@ -111,8 +111,8 @@ suite("Auto Completion Tests", () => { }); describe('Failure tests', function(){ - - it('Autocompletion has no results on value when they are not available', (done) => { + + it('Autocompletion has no results on value when they are not available', (done) => { let content = "time: " let completion = parseSetup(content, 6); completion.then(function(result){ @@ -120,7 +120,7 @@ suite("Auto Completion Tests", () => { }).then(done, done); }); - it('Autocompletion has no results on value when they are not available (with depth)', (done) => { + it('Autocompletion has no results on value when they are not available (with depth)', (done) => { let content = "archive:\n exclude:\n - test\n " let completion = parseSetup(content, 33); completion.then(function(result){ @@ -128,7 +128,7 @@ suite("Auto Completion Tests", () => { }).then(done, done); }); - it('Autocompletion does not complete on wrong spot in array node', (done) => { + it('Autocompletion does not complete on wrong spot in array node', (done) => { let content = "authors:\n - name: test\n " let completion = parseSetup(content, 24); completion.then(function(result){ @@ -141,55 +141,55 @@ suite("Auto Completion Tests", () => { }); }); + +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; - } - 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. - if(lineOffset[linePos+1]){ - newText = document.getText().substring(0, start+(textLine.length-1)) + "holder:\r\n" + document.getText().substr(end+2); - }else{ - newText = document.getText().substring(0, start+(textLine.length)) + "holder:\r\n" + document.getText().substr(end+2); - } - + + //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) + "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 - if(lineOffset[linePos+1]){ - newText = document.getText().substring(0, start+(textLine.length-1)) + ":\r\n" + document.getText().substr(end+2); - }else{ - newText = document.getText().substring(0, start+(textLine.length)) + ":\r\n" + document.getText().substr(end+2); - } - } - 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); + //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); + } } \ No newline at end of file diff --git a/test/integration.test.ts b/test/integration.test.ts index 62b7bb6a..963f3f8f 100644 --- a/test/integration.test.ts +++ b/test/integration.test.ts @@ -40,7 +40,7 @@ suite("Kubernetes Integration Tests", () => { // Tests for validator describe('Yaml Validation with kubernetes', function() { - + function setup(content: string){ return TextDocument.create("file://~/Desktop/vscode-k8s/test.yaml", "yaml", 0, content); } @@ -58,7 +58,7 @@ suite("Kubernetes Integration Tests", () => { //Validating basic nodes describe('Test that validation does not throw errors', function(){ - + it('Basic test', (done) => { let content = `apiVersion: v1`; let validator = parseSetup(content); @@ -89,7 +89,7 @@ suite("Kubernetes Integration Tests", () => { validator.then(function(result){ assert.equal(result.length, 0); }).then(done, done); - }); + }); describe('Type tests', function(){ @@ -115,7 +115,7 @@ suite("Kubernetes Integration Tests", () => { validator.then(function(result){ assert.equal(result.length, 0); }).then(done, done); - }); + }); it('Type Object does not error on valid node', (done) => { let content = `metadata:\n clusterName: tes`; @@ -123,7 +123,7 @@ suite("Kubernetes Integration Tests", () => { validator.then(function(result){ assert.equal(result.length, 0); }).then(done, done); - }); + }); it('Type Array does not error on valid node', (done) => { let content = `items:\n - apiVersion: v1`; @@ -132,10 +132,10 @@ suite("Kubernetes Integration Tests", () => { assert.equal(result.length, 0); }).then(done, done); }); - + }); - }); + }); describe('Test that validation DOES throw errors', function(){ it('Error when theres no value for a node', (done) => { @@ -196,13 +196,13 @@ suite("Kubernetes Integration Tests", () => { }); }); - + }); describe('yamlCompletion with kubernetes', function(){ - + describe('doComplete', function(){ - + function setup(content: string){ return TextDocument.create("file://~/Desktop/vscode-k8s/test.yaml", "yaml", 0, content); } @@ -222,7 +222,7 @@ suite("Kubernetes Integration Tests", () => { let content = ""; let completion = parseSetup(content, 0); completion.then(function(result){ - assert.notEqual(result.items.length, 0); + assert.notEqual(result.items.length, 0); }).then(done, done); }); @@ -265,7 +265,7 @@ suite("Kubernetes Integration Tests", () => { assert.equal(result.items.length, 2); }).then(done, done); }); - + it('Autocomplete key in middle of file', (done) => { let content = "metadata:\n nam"; let completion = parseSetup(content, 14); @@ -274,7 +274,7 @@ suite("Kubernetes Integration Tests", () => { }).then(done, done); }); - it('Autocomplete key in middle of file 2', (done) => { + it('Autocomplete key in middle of file 2', (done) => { let content = "metadata:\n name: test\n cluster"; let completion = parseSetup(content, 31); completion.then(function(result){ @@ -286,12 +286,17 @@ suite("Kubernetes Integration Tests", () => { }); + +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 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]){ @@ -299,33 +304,28 @@ function completionHelper(document: TextDocument, textDocumentPosition){ }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. - if(lineOffset[linePos+1]){ - newText = document.getText().substring(0, start+(textLine.length-1)) + "holder:\r\n" + document.getText().substr(end+2); - }else{ - newText = document.getText().substring(0, start+(textLine.length)) + "holder:\r\n" + document.getText().substr(end+2); - } - - //For when missing semi colon case + newText = document.getText().substring(0, start+textLine.length) + "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 - if(lineOffset[linePos+1]){ - newText = document.getText().substring(0, start+(textLine.length-1)) + ":\r\n" + document.getText().substr(end+2); - }else{ - newText = document.getText().substring(0, start+(textLine.length)) + ":\r\n" + document.getText().substr(end+2); - } + 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);