From 372b0fec8ab35c109ec5db269dfc202bad1fd3ff Mon Sep 17 00:00:00 2001 From: Frederik Ring Date: Sun, 27 May 2018 19:37:57 +0200 Subject: [PATCH] When using go-languageserver, also use code completion feature (#1607) * When using go-languageserver, also use code completion Instead of using go-code, use go-languageserver for providing code completion results when enabled. Solves #1593 * Add naive feature detect for language server feature In case executing the binary with an unknown flag does not yield the -gocodecompletion flag in the help output, we assume the version installed is out of date and prompt for updating * Also consider case where -help might be added at some point in the future * Pass func snippet completion pref, fall back to non-server completion when unsupported * Check supported language server flags and filter before passing * Use initializationOptions for triggering langserver based features * Revert changes to langserver detection, merge onReady callbacks, show message about outdated langserver version * Refactoring * stricter checks --- README.md | 2 +- package.json | 8 +++++++- src/goInstallTools.ts | 7 +------ src/goMain.ts | 45 +++++++++++++++++++++++++++++++++---------- 4 files changed, 44 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 37935d6e7..dae7cd178 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ The Go extension is ready to use on the get go. If you want to customize the fea The Go extension uses a host of Go tools to provide the various language features. An alternative is to use a single language server that provides the same feature. -Set `go.useLanguageServer` to `true` to use the Go language server from [Sourcegraph](https://github.com/sourcegraph/go-langserver) for features like Hover, Definition, Find All References, Signature Help, Go to Symbol in File and Workspace. +Set `go.useLanguageServer` to `true` to use the Go language server from [Sourcegraph](https://github.com/sourcegraph/go-langserver) for features like Code completion, Hover, Definition, Find All References, Signature Help, Go to Symbol in File and Workspace. * This is an experimental feature and is not available in Windows yet. * Since only a single language server is spun up for given VS Code instance, having multi-root setup where the folders have different GOPATH is not supported. * If set to true, you will be prompted to install the Go language server. Once installed, you will have to reload VS Code window. The language server will then be run by the Go extension in the background to provide services needed for the above mentioned features. diff --git a/package.json b/package.json index 7026a395a..b4c9a7405 100644 --- a/package.json +++ b/package.json @@ -702,10 +702,16 @@ "type": "boolean", "default": false, "description": "If true, gofmt is used by the language server to format files." + }, + "autoComplete": { + "type": "boolean", + "default": false, + "description": "If true, the language server will provide code completion results." } }, "default": { - "format": false + "format": false, + "autoComplete": false }, "description": "Use this setting to enable/disable experimental features from the language server." }, diff --git a/src/goInstallTools.ts b/src/goInstallTools.ts index a6216056e..eb519f9ac 100644 --- a/src/goInstallTools.ts +++ b/src/goInstallTools.ts @@ -277,7 +277,7 @@ function installTools(goVersion: SemVersion, missing?: string[]) { outputChannel.appendLine(''); // Blank line for spacing let failures = res.filter(x => x != null); if (failures.length === 0) { - if (missing.indexOf('langserver-go') > -1) { + if (missing.indexOf('go-langserver') > -1) { outputChannel.appendLine('Reload VS Code window to use the Go language server'); } outputChannel.appendLine('All tools successfully installed. You\'re ready to Go :).'); @@ -411,8 +411,3 @@ function allFoldersHaveSameGopath(): boolean { let tempGopath = getCurrentGoPath(vscode.workspace.workspaceFolders[0].uri); return vscode.workspace.workspaceFolders.find(x => tempGopath !== getCurrentGoPath(x.uri)) ? false : true; } - - - - - diff --git a/src/goMain.ts b/src/goMain.ts index 666644214..a2c19cccf 100644 --- a/src/goMain.ts +++ b/src/goMain.ts @@ -30,7 +30,7 @@ import * as goGenerateTests from './goGenerateTests'; import { addImport } from './goImport'; import { installAllTools, checkLanguageServer } from './goInstallTools'; import { isGoPathSet, getBinPath, sendTelemetryEvent, getExtensionCommands, getGoVersion, getCurrentGoPath, getToolsGopath, handleDiagnosticErrors, disposeTelemetryReporter, getToolsEnvVars } from './util'; -import { LanguageClient, RevealOutputChannelOn, FormattingOptions, ProvideDocumentFormattingEditsSignature } from 'vscode-languageclient'; +import { LanguageClient, RevealOutputChannelOn, FormattingOptions, ProvideDocumentFormattingEditsSignature, ProvideCompletionItemsSignature } from 'vscode-languageclient'; import { clearCacheForTools, fixDriveCasingInWindows } from './goPath'; import { addTags, removeTags } from './goModifytags'; import { runFillStruct } from './goFillStruct'; @@ -52,10 +52,8 @@ export let warningDiagnosticCollection: vscode.DiagnosticCollection; export function activate(ctx: vscode.ExtensionContext): void { let useLangServer = vscode.workspace.getConfiguration('go')['useLanguageServer']; - let langServerFlags: string[] = vscode.workspace.getConfiguration('go')['languageServerFlags'] || []; updateGoPathGoRootFromConfig().then(() => { - const languageServerExperimentalFeatures = vscode.workspace.getConfiguration('go').get('languageServerExperimentalFeatures') || {}; const updateToolsCmdText = 'Update tools'; const prevGoroot = ctx.globalState.get('goroot'); const currentGoroot = process.env['GOROOT']; @@ -87,9 +85,10 @@ export function activate(ctx: vscode.ExtensionContext): void { ctx.globalState.update('goroot', currentGoroot); offerToInstallTools(); - let langServerAvailable = checkLanguageServer(); - if (langServerAvailable) { + if (checkLanguageServer()) { + const languageServerExperimentalFeatures = vscode.workspace.getConfiguration('go').get('languageServerExperimentalFeatures') || {}; let langServerFlags: string[] = vscode.workspace.getConfiguration('go')['languageServerFlags'] || []; + const c = new LanguageClient( 'go-langserver', { @@ -100,6 +99,10 @@ export function activate(ctx: vscode.ExtensionContext): void { } }, { + initializationOptions: { + funcSnippetEnabled: vscode.workspace.getConfiguration('go')['useCodeSnippetsOnFunctionSuggest'], + gocodeCompletionEnabled: languageServerExperimentalFeatures['autoComplete'] + }, documentSelector: ['go'], uriConverters: { // Apply file:/// scheme to all file paths. @@ -113,20 +116,43 @@ export function activate(ctx: vscode.ExtensionContext): void { return next(document, options, token); } return []; + }, + provideCompletionItem: (document: vscode.TextDocument, position: vscode.Position, context: vscode.CompletionContext, token: vscode.CancellationToken, next: ProvideCompletionItemsSignature) => { + if (languageServerExperimentalFeatures['autoComplete'] === true) { + return next(document, position, context, token); + } + return []; } } } ); - ctx.subscriptions.push(c.start()); - c.onReady().then(() => { - if (!languageServerExperimentalFeatures['format'] || - !(c.initializeResult && c.initializeResult.capabilities && c.initializeResult.capabilities.documentFormattingProvider)) { + const capabilities = c.initializeResult && c.initializeResult.capabilities; + if (!capabilities) { + return vscode.window.showErrorMessage('The language server is not able to serve any features at the moment.'); + } + + const outdatedMsg = `Your installed version of "go-langserver" is out of date and does not support {0}. Falling back to default behavior.`; + + if (languageServerExperimentalFeatures['autoComplete'] !== true || !capabilities.completionProvider) { + ctx.subscriptions.push(vscode.languages.registerCompletionItemProvider(GO_MODE, new GoCompletionItemProvider(), '.', '\"')); + if (languageServerExperimentalFeatures['autoComplete'] === true) { + vscode.window.showInformationMessage(outdatedMsg.replace('{0}', 'code completion')); + } + } + + if (languageServerExperimentalFeatures['format'] !== true || !capabilities.documentFormattingProvider) { ctx.subscriptions.push(vscode.languages.registerDocumentFormattingEditProvider(GO_MODE, new GoDocumentFormattingEditProvider())); + if (languageServerExperimentalFeatures['format'] === true) { + vscode.window.showInformationMessage(outdatedMsg.replace('{0}', 'code formatting')); + } } }); + + ctx.subscriptions.push(c.start()); } else { + ctx.subscriptions.push(vscode.languages.registerCompletionItemProvider(GO_MODE, new GoCompletionItemProvider(), '.', '\"')); ctx.subscriptions.push(vscode.languages.registerHoverProvider(GO_MODE, new GoHoverProvider())); ctx.subscriptions.push(vscode.languages.registerDefinitionProvider(GO_MODE, new GoDefinitionProvider())); ctx.subscriptions.push(vscode.languages.registerReferenceProvider(GO_MODE, new GoReferenceProvider())); @@ -147,7 +173,6 @@ export function activate(ctx: vscode.ExtensionContext): void { let testCodeLensProvider = new GoRunTestCodeLensProvider(); let referencesCodeLensProvider = new GoReferencesCodeLensProvider(); - ctx.subscriptions.push(vscode.languages.registerCompletionItemProvider(GO_MODE, new GoCompletionItemProvider(), '.', '\"')); ctx.subscriptions.push(vscode.languages.registerRenameProvider(GO_MODE, new GoRenameProvider())); ctx.subscriptions.push(vscode.languages.registerCodeActionsProvider(GO_MODE, new GoCodeActionProvider())); ctx.subscriptions.push(vscode.languages.registerCodeLensProvider(GO_MODE, testCodeLensProvider));