Skip to content
This repository has been archived by the owner on Jul 15, 2023. It is now read-only.

Triggering autocompletion in comments if the next item is an exported member #1005 #1675

Merged
merged 15 commits into from
May 15, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions src/goSuggest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ interface GoCodeSuggestion {
type: string;
}

const lineCommentRegex = /^\s*\/\/\s+/;
const exportedMemberRegex = /(const|func|type|var)\s+([A-Z]\w*)/;

export class GoCompletionItemProvider implements vscode.CompletionItemProvider {

private pkgsList = new Map<string, string>();
Expand All @@ -52,7 +55,17 @@ export class GoCompletionItemProvider implements vscode.CompletionItemProvider {
let lineTillCurrentPosition = lineText.substr(0, position.character);
let autocompleteUnimportedPackages = config['autocompleteUnimportedPackages'] === true && !lineText.match(/^(\s)*(import|package)(\s)+/);

// prevent completion when typing in a line comment
// triggering completions in comments on exported members
if (lineCommentRegex.test(lineTillCurrentPosition) && position.line + 1 < document.lineCount) {
let nextLine = document.lineAt(position.line + 1).text.trim();
let memberType = nextLine.match(exportedMemberRegex);
let suggestionItem: vscode.CompletionItem;
if (memberType && memberType.length === 3) {
suggestionItem = new vscode.CompletionItem(memberType[2], vscodeKindFromGoCodeClass(memberType[1]));
}
return resolve(suggestionItem ? [suggestionItem] : []);
}
// prevent completion when typing in a line comment that doesnt start from the beginning of the line
const commentIndex = lineText.indexOf('//');
if (commentIndex >= 0 && position.character > commentIndex) {
return resolve([]);
Expand Down Expand Up @@ -345,4 +358,4 @@ export class GoCompletionItemProvider implements vscode.CompletionItemProvider {
return matchingPackages[0];
}
}
}
}
21 changes: 21 additions & 0 deletions test/fixtures/completions/exportedMemberDocs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package main

import (
"fmt"
)

// L
var Language = "english" // should not invoke completion since this is line comment

// G
const GreetingText = "Hello"

// S
func SayHello() {
fmt.Println("Says hello!")
}

// HelloParams
type HelloParams struct {
language string
}
92 changes: 63 additions & 29 deletions test/go.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ suite('Go Extension Tests', () => {
fs.copySync(path.join(fixtureSourcePath, 'buildTags', 'hello.go'), path.join(fixturePath, 'buildTags', 'hello.go'));
fs.copySync(path.join(fixtureSourcePath, 'completions', 'unimportedPkgs.go'), path.join(fixturePath, 'completions', 'unimportedPkgs.go'));
fs.copySync(path.join(fixtureSourcePath, 'completions', 'snippets.go'), path.join(fixturePath, 'completions', 'snippets.go'));
fs.copySync(path.join(fixtureSourcePath, 'completions', 'exportedMemberDocs.go'), path.join(fixturePath, 'completions', 'exportedMemberDocs.go'));
fs.copySync(path.join(fixtureSourcePath, 'importTest', 'noimports.go'), path.join(fixturePath, 'importTest', 'noimports.go'));
fs.copySync(path.join(fixtureSourcePath, 'importTest', 'groupImports.go'), path.join(fixturePath, 'importTest', 'groupImports.go'));
fs.copySync(path.join(fixtureSourcePath, 'importTest', 'singleImports.go'), path.join(fixturePath, 'importTest', 'singleImports.go'));
Expand Down Expand Up @@ -278,9 +279,9 @@ It returns the number of bytes written and any write error encountered.
for (let i in expected) {
for (let j in sortedDiagnostics) {
if (expected[i].line
&& (expected[i].line === sortedDiagnostics[j].line)
&& (expected[i].severity === sortedDiagnostics[j].severity)
&& (expected[i].msg === sortedDiagnostics[j].msg)) {
&& (expected[i].line === sortedDiagnostics[j].line)
&& (expected[i].severity === sortedDiagnostics[j].severity)
&& (expected[i].msg === sortedDiagnostics[j].msg)) {
matchCount++;
}
}
Expand Down Expand Up @@ -404,8 +405,8 @@ It returns the number of bytes written and any write error encountered.
for (let i in expected) {
for (let j in sortedDiagnostics) {
if ((expected[i].line === sortedDiagnostics[j].line)
&& (expected[i].severity === sortedDiagnostics[j].severity)
&& (expected[i].msg === sortedDiagnostics[j].msg)) {
&& (expected[i].severity === sortedDiagnostics[j].severity)
&& (expected[i].msg === sortedDiagnostics[j].msg)) {
matchCount++;
}
}
Expand Down Expand Up @@ -776,36 +777,36 @@ It returns the number of bytes written and any write error encountered.
vscode.workspace.openTextDocument(uri).then((textDocument) => {
return vscode.window.showTextDocument(textDocument).then(editor => {

let noFunctionSnippet = provider.provideCompletionItemsInternal(editor.document, new vscode.Position(9, 6), null, Object.create(baseConfig, {'useCodeSnippetsOnFunctionSuggest': { value: false }})).then(items => {
let item = items.find(x => x.label === 'Print');
assert.equal(!item.insertText, true);
});
let noFunctionSnippet = provider.provideCompletionItemsInternal(editor.document, new vscode.Position(9, 6), null, Object.create(baseConfig, { 'useCodeSnippetsOnFunctionSuggest': { value: false } })).then(items => {
let item = items.find(x => x.label === 'Print');
assert.equal(!item.insertText, true);
});

let withFunctionSnippet = provider.provideCompletionItemsInternal(editor.document, new vscode.Position(9, 6), null, Object.create(baseConfig, {'useCodeSnippetsOnFunctionSuggest': { value: true }})).then(items => {
let item = items.find(x => x.label === 'Print');
assert.equal((<vscode.SnippetString>item.insertText).value, 'Print(${1:a ...interface{\\}})');
let withFunctionSnippet = provider.provideCompletionItemsInternal(editor.document, new vscode.Position(9, 6), null, Object.create(baseConfig, { 'useCodeSnippetsOnFunctionSuggest': { value: true } })).then(items => {
let item = items.find(x => x.label === 'Print');
assert.equal((<vscode.SnippetString>item.insertText).value, 'Print(${1:a ...interface{\\}})');

});
});

let withFunctionSnippetNotype = provider.provideCompletionItemsInternal(editor.document, new vscode.Position(9, 6), null, Object.create(baseConfig, {'useCodeSnippetsOnFunctionSuggestWithoutType': { value: true }})).then(items => {
let item = items.find(x => x.label === 'Print');
assert.equal((<vscode.SnippetString>item.insertText).value, 'Print(${1:a})');
});
let withFunctionSnippetNotype = provider.provideCompletionItemsInternal(editor.document, new vscode.Position(9, 6), null, Object.create(baseConfig, { 'useCodeSnippetsOnFunctionSuggestWithoutType': { value: true } })).then(items => {
let item = items.find(x => x.label === 'Print');
assert.equal((<vscode.SnippetString>item.insertText).value, 'Print(${1:a})');
});

let noFunctionAsVarSnippet = provider.provideCompletionItemsInternal(editor.document, new vscode.Position(11, 3), null, Object.create(baseConfig, {'useCodeSnippetsOnFunctionSuggest': { value: false }})).then(items => {
let item = items.find(x => x.label === 'funcAsVariable');
assert.equal(!item.insertText, true);
});
let noFunctionAsVarSnippet = provider.provideCompletionItemsInternal(editor.document, new vscode.Position(11, 3), null, Object.create(baseConfig, { 'useCodeSnippetsOnFunctionSuggest': { value: false } })).then(items => {
let item = items.find(x => x.label === 'funcAsVariable');
assert.equal(!item.insertText, true);
});

let withFunctionAsVarSnippet = provider.provideCompletionItemsInternal(editor.document, new vscode.Position(11, 3), null, Object.create(baseConfig, {'useCodeSnippetsOnFunctionSuggest': { value: true }})).then(items => {
let item = items.find(x => x.label === 'funcAsVariable');
assert.equal((<vscode.SnippetString>item.insertText).value, 'funcAsVariable(${1:k string})');
});
let withFunctionAsVarSnippet = provider.provideCompletionItemsInternal(editor.document, new vscode.Position(11, 3), null, Object.create(baseConfig, { 'useCodeSnippetsOnFunctionSuggest': { value: true } })).then(items => {
let item = items.find(x => x.label === 'funcAsVariable');
assert.equal((<vscode.SnippetString>item.insertText).value, 'funcAsVariable(${1:k string})');
});

let withFunctionAsVarSnippetNoType = provider.provideCompletionItemsInternal(editor.document, new vscode.Position(11, 3), null, Object.create(baseConfig, {'useCodeSnippetsOnFunctionSuggestWithoutType': { value: true }})).then(items => {
let item = items.find(x => x.label === 'funcAsVariable');
assert.equal((<vscode.SnippetString>item.insertText).value, 'funcAsVariable(${1:k})');
});
let withFunctionAsVarSnippetNoType = provider.provideCompletionItemsInternal(editor.document, new vscode.Position(11, 3), null, Object.create(baseConfig, { 'useCodeSnippetsOnFunctionSuggestWithoutType': { value: true } })).then(items => {
let item = items.find(x => x.label === 'funcAsVariable');
assert.equal((<vscode.SnippetString>item.insertText).value, 'funcAsVariable(${1:k})');
});

let noFunctionAsTypeSnippet = provider.provideCompletionItemsInternal(editor.document, new vscode.Position(14, 0), null, Object.create(baseConfig, {'useCodeSnippetsOnFunctionSuggest': { value: false }})).then(items => {
let item1 = items.find(x => x.label === 'HandlerFunc');
Expand Down Expand Up @@ -863,6 +864,39 @@ It returns the number of bytes written and any write error encountered.
}).then(() => done(), done);
});

test('Test Completion on Comments for Exported Members', (done) => {
let provider = new GoCompletionItemProvider();
let testCases: [vscode.Position, string[]][] = [
[new vscode.Position(6, 4), ['Language']],
[new vscode.Position(9, 4), ['GreetingText']],
// checking for comment completions with begining of comment without space
[new vscode.Position(12, 2), []],
// cursor between /$/ this should not trigger any completion
[new vscode.Position(12, 1), []],
[new vscode.Position(12, 4), ['SayHello']],
[new vscode.Position(17, 5), ['HelloParams']],
];
let uri = vscode.Uri.file(path.join(fixturePath, 'completions', 'exportedMemberDocs.go'));

vscode.workspace.openTextDocument(uri).then((textDocument) => {
return vscode.window.showTextDocument(textDocument).then(editor => {
let promises = testCases.map(([position, expected]) =>
provider.provideCompletionItems(editor.document, position, null).then(items => {
let labels = items.map(x => x.label);
assert.equal(expected.length, labels.length, `expected number of completions: ${expected.length} Actual: ${labels.length} at position(${position.line},${position.character}) ${labels}`);
expected.forEach((entry, index) => {
assert.equal(entry, labels[index], `mismatch in comment completion list Expected: ${entry} Actual: ${labels[index]}`);
});

})
);
return Promise.all(promises).then(() => vscode.commands.executeCommand('workbench.action.closeActiveEditor'));
});
}, (err) => {
assert.ok(false, `error in OpenTextDocument ${err}`);
}).then(() => done(), done);
});

test('getImportPath()', () => {
let testCases: [string, string][] = [
['import "github.com/sirupsen/logrus"', 'github.com/sirupsen/logrus'],
Expand Down