| /* eslint-disable @typescript-eslint/no-unused-vars */ |
| /* eslint-disable @typescript-eslint/no-explicit-any */ |
| /*--------------------------------------------------------- |
| * Copyright (C) Microsoft Corporation. All rights reserved. |
| * Modification copyright 2020 The Go Authors. All rights reserved. |
| * Licensed under the MIT License. See LICENSE in the project root for license information. |
| *--------------------------------------------------------*/ |
| |
| 'use strict'; |
| |
| import * as path from 'path'; |
| import { getGoConfig, getGoplsConfig, IsInCloudIDE } from './config'; |
| import { browsePackages } from './goBrowsePackage'; |
| import { buildCode } from './goBuild'; |
| import { check, notifyIfGeneratedFile, removeTestStatus } from './goCheck'; |
| import { |
| applyCodeCoverage, |
| applyCodeCoverageToAllEditors, |
| initCoverageDecorators, |
| removeCodeCoverageOnFileSave, |
| toggleCoverageCurrentPackage, |
| trackCodeCoverageRemovalOnFileChange, |
| updateCodeCoverageDecorators |
| } from './goCover'; |
| import { GoDebugConfigurationProvider } from './goDebugConfiguration'; |
| import { GoDebugAdapterDescriptorFactory, GoDebugAdapterTrackerFactory } from './goDebugFactory'; |
| import { extractFunction, extractVariable } from './goDoctor'; |
| import { toolExecutionEnvironment } from './goEnv'; |
| import { |
| chooseGoEnvironment, |
| offerToInstallLatestGoVersion, |
| setEnvironmentVariableCollection |
| } from './goEnvironmentStatus'; |
| import { runFillStruct } from './goFillStruct'; |
| import * as goGenerateTests from './goGenerateTests'; |
| import { goGetPackage } from './goGetPackage'; |
| import { implCursor } from './goImpl'; |
| import { addImport, addImportToWorkspace } from './goImport'; |
| import { installCurrentPackage } from './goInstall'; |
| import { |
| installAllTools, |
| installTools, |
| offerToInstallTools, |
| promptForMissingTool, |
| updateGoVarsFromConfig |
| } from './goInstallTools'; |
| import { |
| isInPreviewMode, |
| languageServerIsRunning, |
| showServerOutputChannel, |
| startLanguageServerWithFallback, |
| watchLanguageServerConfiguration |
| } from './goLanguageServer'; |
| import { lintCode } from './goLint'; |
| import { logVerbose, setLogConfig } from './goLogging'; |
| import { GO_MODE } from './goMode'; |
| import { addTags, removeTags } from './goModifytags'; |
| import { GO111MODULE, goModInit, isModSupported } from './goModules'; |
| import { playgroundCommand } from './goPlayground'; |
| import { GoReferencesCodeLensProvider } from './goReferencesCodelens'; |
| import { GoRunTestCodeLensProvider } from './goRunTestCodelens'; |
| import { disposeGoStatusBar, expandGoStatusBar, outputChannel, updateGoStatusBar } from './goStatus'; |
| import { |
| debugPrevious, |
| subTestAtCursor, |
| testAtCursor, |
| testAtCursorOrPrevious, |
| testCurrentFile, |
| testCurrentPackage, |
| testPrevious, |
| testWorkspace |
| } from './goTest'; |
| import { getConfiguredTools } from './goTools'; |
| import { vetCode } from './goVet'; |
| import { pickGoProcess, pickProcess } from './pickProcess'; |
| import { |
| getFromGlobalState, |
| getFromWorkspaceState, |
| resetGlobalState, |
| resetWorkspaceState, |
| setGlobalState, |
| setWorkspaceState, |
| updateGlobalState, |
| updateWorkspaceState |
| } from './stateUtils'; |
| import { cancelRunningTests, showTestOutput } from './testUtils'; |
| import { |
| cleanupTempDir, |
| getBinPath, |
| getCurrentGoPath, |
| getExtensionCommands, |
| getGoEnv, |
| getGoVersion, |
| getToolsGopath, |
| getWorkspaceFolderPath, |
| GoVersion, |
| handleDiagnosticErrors, |
| isGoPathSet, |
| resolvePath, |
| runGoVersionM |
| } from './util'; |
| import { |
| clearCacheForTools, |
| fileExists, |
| getCurrentGoRoot, |
| dirExists, |
| setCurrentGoRoot, |
| envPath |
| } from './utils/pathUtils'; |
| import { WelcomePanel } from './welcome'; |
| import semver = require('semver'); |
| import vscode = require('vscode'); |
| import { getFormatTool } from './goFormat'; |
| import { resetSurveyConfig, showSurveyConfig, timeMinute } from './goSurvey'; |
| import { ExtensionAPI } from './export'; |
| import extensionAPI from './extensionAPI'; |
| import { GoTestExplorer, isVscodeTestingAPIAvailable } from './goTest/explore'; |
| import { ProfileDocumentContentProvider } from './goToolPprof'; |
| |
| export let buildDiagnosticCollection: vscode.DiagnosticCollection; |
| export let lintDiagnosticCollection: vscode.DiagnosticCollection; |
| export let vetDiagnosticCollection: vscode.DiagnosticCollection; |
| |
| // restartLanguageServer wraps all of the logic needed to restart the |
| // language server. It can be used to enable, disable, or otherwise change |
| // the configuration of the server. |
| export let restartLanguageServer = () => { |
| return; |
| }; |
| |
| export async function activate(ctx: vscode.ExtensionContext): Promise<ExtensionAPI> { |
| if (process.env['VSCODE_GO_IN_TEST'] === '1') { |
| // Make sure this does not run when running in test. |
| return; |
| } |
| |
| setGlobalState(ctx.globalState); |
| setWorkspaceState(ctx.workspaceState); |
| setEnvironmentVariableCollection(ctx.environmentVariableCollection); |
| |
| const cfg = getGoConfig(); |
| setLogConfig(cfg['logging']); |
| |
| if (vscode.window.registerWebviewPanelSerializer) { |
| // Make sure we register a serializer in activation event |
| vscode.window.registerWebviewPanelSerializer(WelcomePanel.viewType, { |
| async deserializeWebviewPanel(webviewPanel: vscode.WebviewPanel, state: any) { |
| WelcomePanel.revive(webviewPanel, ctx.extensionUri); |
| } |
| }); |
| } |
| |
| if (isInPreviewMode()) { |
| // For Nightly extension users, show a message directing them to forums |
| // to give feedback. |
| setTimeout(showGoNightlyWelcomeMessage, 10 * timeMinute); |
| } |
| |
| // Show the Go welcome page on update. |
| if (!IsInCloudIDE) { |
| showGoWelcomePage(ctx); |
| } |
| |
| const configGOROOT = getGoConfig()['goroot']; |
| if (configGOROOT) { |
| // We don't support unsetting go.goroot because we don't know whether |
| // !configGOROOT case indicates the user wants to unset process.env['GOROOT'] |
| // or the user wants the extension to use the current process.env['GOROOT'] value. |
| // TODO(hyangah): consider utilizing an empty value to indicate unset? |
| await setGOROOTEnvVar(configGOROOT); |
| } |
| |
| // Present a warning about the deprecation of the go.documentLink setting. |
| const experimentalFeatures = getGoConfig()['languageServerExperimentalFeatures']; |
| if (experimentalFeatures) { |
| // TODO(golang/vscode-go#50): Eventually notify about deprecation of |
| // all of the settings. See golang/vscode-go#1109 too. |
| // The `diagnostics` setting is still used as a workaround for running custom vet. |
| if (experimentalFeatures['documentLink'] === false) { |
| vscode.window |
| .showErrorMessage(`The 'go.languageServerExperimentalFeature.documentLink' setting is now deprecated. |
| Please use '"gopls": {"ui.navigation.importShortcut": "Definition" }' instead. |
| See [the settings doc](https://github.com/golang/vscode-go/blob/master/docs/settings.md#uinavigationimportshortcut) for more details.`); |
| } |
| const promptKey = 'promptedLanguageServerExperimentalFeatureDeprecation'; |
| const prompted = getFromGlobalState(promptKey, false); |
| if (!prompted && experimentalFeatures['diagnostics'] === false) { |
| const msg = `The 'go.languageServerExperimentalFeature.diagnostics' setting will be deprecated soon. |
| If you would like additional configuration for diagnostics from gopls, please see and response to [Issue 50](https://github.com/golang/vscode-go/issues/50).`; |
| const selected = await vscode.window.showInformationMessage(msg, "Don't show again"); |
| switch (selected) { |
| case "Don't show again": |
| updateGlobalState(promptKey, true); |
| } |
| } |
| } |
| updateGoVarsFromConfig().then(async () => { |
| suggestUpdates(ctx); |
| offerToInstallLatestGoVersion(); |
| offerToInstallTools(); |
| await configureLanguageServer(ctx); |
| |
| if ( |
| !languageServerIsRunning && |
| vscode.window.activeTextEditor && |
| vscode.window.activeTextEditor.document.languageId === 'go' && |
| isGoPathSet() |
| ) { |
| // Check mod status so that cache is updated and then run build/lint/vet |
| isModSupported(vscode.window.activeTextEditor.document.uri).then(() => { |
| runBuilds(vscode.window.activeTextEditor.document, getGoConfig()); |
| }); |
| } |
| }); |
| |
| initCoverageDecorators(ctx); |
| |
| ctx.subscriptions.push( |
| vscode.commands.registerCommand('go.environment.status', async () => { |
| expandGoStatusBar(); |
| }) |
| ); |
| const testCodeLensProvider = new GoRunTestCodeLensProvider(); |
| const referencesCodeLensProvider = new GoReferencesCodeLensProvider(); |
| |
| ctx.subscriptions.push(vscode.languages.registerCodeLensProvider(GO_MODE, testCodeLensProvider)); |
| ctx.subscriptions.push(vscode.languages.registerCodeLensProvider(GO_MODE, referencesCodeLensProvider)); |
| |
| // debug |
| ctx.subscriptions.push( |
| vscode.debug.registerDebugConfigurationProvider('go', new GoDebugConfigurationProvider('go')) |
| ); |
| ctx.subscriptions.push( |
| vscode.commands.registerCommand( |
| 'go.debug.pickProcess', |
| async (): Promise<string> => { |
| return await pickProcess(); |
| } |
| ) |
| ); |
| ctx.subscriptions.push( |
| vscode.commands.registerCommand( |
| 'go.debug.pickGoProcess', |
| async (): Promise<string> => { |
| return await pickGoProcess(); |
| } |
| ) |
| ); |
| |
| const debugOutputChannel = vscode.window.createOutputChannel('Go Debug'); |
| ctx.subscriptions.push(debugOutputChannel); |
| |
| const factory = new GoDebugAdapterDescriptorFactory(debugOutputChannel); |
| ctx.subscriptions.push(vscode.debug.registerDebugAdapterDescriptorFactory('go', factory)); |
| if ('dispose' in factory) { |
| ctx.subscriptions.push(factory); |
| } |
| |
| const tracker = new GoDebugAdapterTrackerFactory(debugOutputChannel); |
| ctx.subscriptions.push(vscode.debug.registerDebugAdapterTrackerFactory('go', tracker)); |
| if ('dispose' in tracker) { |
| ctx.subscriptions.push(tracker); |
| } |
| |
| buildDiagnosticCollection = vscode.languages.createDiagnosticCollection('go'); |
| ctx.subscriptions.push(buildDiagnosticCollection); |
| lintDiagnosticCollection = vscode.languages.createDiagnosticCollection( |
| lintDiagnosticCollectionName(getGoConfig()['lintTool']) |
| ); |
| ctx.subscriptions.push(lintDiagnosticCollection); |
| vetDiagnosticCollection = vscode.languages.createDiagnosticCollection('go-vet'); |
| ctx.subscriptions.push(vetDiagnosticCollection); |
| |
| addOnChangeTextDocumentListeners(ctx); |
| addOnChangeActiveTextEditorListeners(ctx); |
| addOnSaveTextDocumentListeners(ctx); |
| |
| ctx.subscriptions.push( |
| vscode.commands.registerCommand('go.gopath', () => { |
| getCurrentGoPathCommand(); |
| }) |
| ); |
| |
| ctx.subscriptions.push( |
| vscode.commands.registerCommand('go.locate.tools', async () => { |
| getConfiguredGoToolsCommand(); |
| }) |
| ); |
| |
| ctx.subscriptions.push( |
| vscode.commands.registerCommand('go.add.tags', (args) => { |
| addTags(args); |
| }) |
| ); |
| |
| ctx.subscriptions.push( |
| vscode.commands.registerCommand('go.remove.tags', (args) => { |
| removeTags(args); |
| }) |
| ); |
| |
| ctx.subscriptions.push( |
| vscode.commands.registerCommand('go.fill.struct', () => { |
| runFillStruct(vscode.window.activeTextEditor); |
| }) |
| ); |
| |
| ctx.subscriptions.push( |
| vscode.commands.registerCommand('go.impl.cursor', () => { |
| implCursor(); |
| }) |
| ); |
| ctx.subscriptions.push( |
| vscode.commands.registerCommand('go.godoctor.extract', () => { |
| extractFunction(); |
| }) |
| ); |
| ctx.subscriptions.push( |
| vscode.commands.registerCommand('go.godoctor.var', () => { |
| extractVariable(); |
| }) |
| ); |
| |
| ctx.subscriptions.push( |
| vscode.commands.registerCommand('go.test.cursor', (args) => { |
| const goConfig = getGoConfig(); |
| testAtCursor(goConfig, 'test', args); |
| }) |
| ); |
| |
| ctx.subscriptions.push( |
| vscode.commands.registerCommand('go.test.cursorOrPrevious', (args) => { |
| const goConfig = getGoConfig(); |
| testAtCursorOrPrevious(goConfig, 'test', args); |
| }) |
| ); |
| |
| if (isVscodeTestingAPIAvailable) { |
| GoTestExplorer.setup(ctx); |
| } |
| |
| ctx.subscriptions.push( |
| vscode.workspace.registerTextDocumentContentProvider('go-tool-pprof', new ProfileDocumentContentProvider()) |
| ); |
| |
| ctx.subscriptions.push( |
| vscode.commands.registerCommand('go.subtest.cursor', (args) => { |
| const goConfig = getGoConfig(); |
| subTestAtCursor(goConfig, args); |
| }) |
| ); |
| |
| ctx.subscriptions.push( |
| vscode.commands.registerCommand('go.debug.cursor', (args) => { |
| const goConfig = getGoConfig(); |
| testAtCursor(goConfig, 'debug', args); |
| }) |
| ); |
| |
| ctx.subscriptions.push( |
| vscode.commands.registerCommand('go.benchmark.cursor', (args) => { |
| const goConfig = getGoConfig(); |
| testAtCursor(goConfig, 'benchmark', args); |
| }) |
| ); |
| |
| ctx.subscriptions.push( |
| vscode.commands.registerCommand('go.test.package', (args) => { |
| const goConfig = getGoConfig(); |
| const isBenchmark = false; |
| testCurrentPackage(goConfig, isBenchmark, args); |
| }) |
| ); |
| |
| ctx.subscriptions.push( |
| vscode.commands.registerCommand('go.benchmark.package', (args) => { |
| const goConfig = getGoConfig(); |
| const isBenchmark = true; |
| testCurrentPackage(goConfig, isBenchmark, args); |
| }) |
| ); |
| |
| ctx.subscriptions.push( |
| vscode.commands.registerCommand('go.test.file', (args) => { |
| const goConfig = getGoConfig(); |
| const isBenchmark = false; |
| testCurrentFile(goConfig, isBenchmark, args); |
| }) |
| ); |
| |
| ctx.subscriptions.push( |
| vscode.commands.registerCommand('go.benchmark.file', (args) => { |
| const goConfig = getGoConfig(); |
| const isBenchmark = true; |
| testCurrentFile(goConfig, isBenchmark, args); |
| }) |
| ); |
| |
| ctx.subscriptions.push( |
| vscode.commands.registerCommand('go.test.workspace', (args) => { |
| const goConfig = getGoConfig(); |
| testWorkspace(goConfig, args); |
| }) |
| ); |
| |
| ctx.subscriptions.push( |
| vscode.commands.registerCommand('go.test.previous', () => { |
| testPrevious(); |
| }) |
| ); |
| |
| ctx.subscriptions.push( |
| vscode.commands.registerCommand('go.debug.previous', () => { |
| debugPrevious(); |
| }) |
| ); |
| |
| ctx.subscriptions.push( |
| vscode.commands.registerCommand('go.test.coverage', () => { |
| toggleCoverageCurrentPackage(); |
| }) |
| ); |
| |
| ctx.subscriptions.push( |
| vscode.commands.registerCommand('go.test.showOutput', () => { |
| showTestOutput(); |
| }) |
| ); |
| |
| ctx.subscriptions.push( |
| vscode.commands.registerCommand('go.test.cancel', () => { |
| cancelRunningTests(); |
| }) |
| ); |
| |
| ctx.subscriptions.push( |
| vscode.commands.registerCommand('go.import.add', (arg) => { |
| return addImport(arg); |
| }) |
| ); |
| |
| ctx.subscriptions.push( |
| vscode.commands.registerCommand('go.add.package.workspace', () => { |
| addImportToWorkspace(); |
| }) |
| ); |
| |
| ctx.subscriptions.push( |
| vscode.commands.registerCommand('go.tools.install', async (args) => { |
| if (Array.isArray(args) && args.length) { |
| const goVersion = await getGoVersion(); |
| await installTools(args, goVersion); |
| return; |
| } |
| installAllTools(); |
| }) |
| ); |
| |
| ctx.subscriptions.push( |
| vscode.commands.registerCommand('go.browse.packages', () => { |
| browsePackages(); |
| }) |
| ); |
| |
| ctx.subscriptions.push( |
| vscode.workspace.onDidChangeConfiguration(async (e: vscode.ConfigurationChangeEvent) => { |
| if (!e.affectsConfiguration('go')) { |
| return; |
| } |
| const updatedGoConfig = getGoConfig(); |
| |
| if (e.affectsConfiguration('go.goroot')) { |
| const configGOROOT = updatedGoConfig['goroot']; |
| if (configGOROOT) { |
| await setGOROOTEnvVar(configGOROOT); |
| } |
| } |
| if ( |
| e.affectsConfiguration('go.goroot') || |
| e.affectsConfiguration('go.alternateTools') || |
| e.affectsConfiguration('go.gopath') || |
| e.affectsConfiguration('go.toolsEnvVars') || |
| e.affectsConfiguration('go.testEnvFile') |
| ) { |
| updateGoVarsFromConfig(); |
| } |
| if (e.affectsConfiguration('go.logging')) { |
| setLogConfig(updatedGoConfig['logging']); |
| } |
| // If there was a change in "toolsGopath" setting, then clear cache for go tools |
| if (getToolsGopath() !== getToolsGopath(false)) { |
| clearCacheForTools(); |
| } |
| |
| if (updatedGoConfig['enableCodeLens']) { |
| testCodeLensProvider.setEnabled(updatedGoConfig['enableCodeLens']['runtest']); |
| referencesCodeLensProvider.setEnabled(updatedGoConfig['enableCodeLens']['references']); |
| } |
| |
| if (e.affectsConfiguration('go.formatTool')) { |
| checkToolExists(getFormatTool(updatedGoConfig)); |
| } |
| if (e.affectsConfiguration('go.lintTool')) { |
| checkToolExists(updatedGoConfig['lintTool']); |
| } |
| if (e.affectsConfiguration('go.docsTool')) { |
| checkToolExists(updatedGoConfig['docsTool']); |
| } |
| if (e.affectsConfiguration('go.coverageDecorator')) { |
| updateCodeCoverageDecorators(updatedGoConfig['coverageDecorator']); |
| } |
| if (e.affectsConfiguration('go.toolsEnvVars')) { |
| const env = toolExecutionEnvironment(); |
| if (GO111MODULE !== env['GO111MODULE']) { |
| const reloadMsg = |
| 'Reload VS Code window so that the Go tools can respect the change to GO111MODULE'; |
| vscode.window.showInformationMessage(reloadMsg, 'Reload').then((selected) => { |
| if (selected === 'Reload') { |
| vscode.commands.executeCommand('workbench.action.reloadWindow'); |
| } |
| }); |
| } |
| } |
| if (e.affectsConfiguration('go.lintTool')) { |
| const lintTool = lintDiagnosticCollectionName(updatedGoConfig['lintTool']); |
| if (lintDiagnosticCollection && lintDiagnosticCollection.name !== lintTool) { |
| lintDiagnosticCollection.dispose(); |
| lintDiagnosticCollection = vscode.languages.createDiagnosticCollection(lintTool); |
| ctx.subscriptions.push(lintDiagnosticCollection); |
| // TODO: actively maintain our own disposables instead of keeping pushing to ctx.subscription. |
| } |
| } |
| }) |
| ); |
| |
| ctx.subscriptions.push( |
| vscode.commands.registerCommand('go.test.generate.package', () => { |
| goGenerateTests.generateTestCurrentPackage(); |
| }) |
| ); |
| |
| ctx.subscriptions.push( |
| vscode.commands.registerCommand('go.test.generate.file', () => { |
| goGenerateTests.generateTestCurrentFile(); |
| }) |
| ); |
| |
| ctx.subscriptions.push( |
| vscode.commands.registerCommand('go.test.generate.function', () => { |
| goGenerateTests.generateTestCurrentFunction(); |
| }) |
| ); |
| |
| ctx.subscriptions.push( |
| vscode.commands.registerCommand('go.toggle.test.file', () => { |
| goGenerateTests.toggleTestFile(); |
| }) |
| ); |
| |
| ctx.subscriptions.push( |
| vscode.commands.registerCommand('go.debug.startSession', (config) => { |
| let workspaceFolder; |
| if (vscode.window.activeTextEditor) { |
| workspaceFolder = vscode.workspace.getWorkspaceFolder(vscode.window.activeTextEditor.document.uri); |
| } |
| |
| return vscode.debug.startDebugging(workspaceFolder, config); |
| }) |
| ); |
| |
| ctx.subscriptions.push( |
| vscode.commands.registerCommand('go.show.commands', () => { |
| const extCommands = getExtensionCommands(); |
| extCommands.push({ |
| command: 'editor.action.goToDeclaration', |
| title: 'Go to Definition' |
| }); |
| extCommands.push({ |
| command: 'editor.action.goToImplementation', |
| title: 'Go to Implementation' |
| }); |
| extCommands.push({ |
| command: 'workbench.action.gotoSymbol', |
| title: 'Go to Symbol in File...' |
| }); |
| extCommands.push({ |
| command: 'workbench.action.showAllSymbols', |
| title: 'Go to Symbol in Workspace...' |
| }); |
| vscode.window.showQuickPick(extCommands.map((x) => x.title)).then((cmd) => { |
| const selectedCmd = extCommands.find((x) => x.title === cmd); |
| if (selectedCmd) { |
| vscode.commands.executeCommand(selectedCmd.command); |
| } |
| }); |
| }) |
| ); |
| |
| ctx.subscriptions.push(vscode.commands.registerCommand('go.get.package', goGetPackage)); |
| |
| ctx.subscriptions.push(vscode.commands.registerCommand('go.playground', playgroundCommand)); |
| |
| ctx.subscriptions.push(vscode.commands.registerCommand('go.lint.package', () => lintCode('package'))); |
| |
| ctx.subscriptions.push(vscode.commands.registerCommand('go.lint.workspace', () => lintCode('workspace'))); |
| |
| ctx.subscriptions.push(vscode.commands.registerCommand('go.lint.file', () => lintCode('file'))); |
| |
| ctx.subscriptions.push(vscode.commands.registerCommand('go.vet.package', vetCode)); |
| |
| ctx.subscriptions.push(vscode.commands.registerCommand('go.vet.workspace', () => vetCode(true))); |
| |
| ctx.subscriptions.push(vscode.commands.registerCommand('go.build.package', buildCode)); |
| |
| ctx.subscriptions.push(vscode.commands.registerCommand('go.build.workspace', () => buildCode(true))); |
| |
| ctx.subscriptions.push(vscode.commands.registerCommand('go.install.package', installCurrentPackage)); |
| |
| ctx.subscriptions.push(vscode.commands.registerCommand('go.run.modinit', goModInit)); |
| |
| ctx.subscriptions.push( |
| vscode.commands.registerCommand('go.extractServerChannel', () => { |
| showServerOutputChannel(); |
| }) |
| ); |
| |
| ctx.subscriptions.push( |
| vscode.commands.registerCommand('go.welcome', () => { |
| WelcomePanel.createOrShow(ctx.extensionUri); |
| }) |
| ); |
| |
| ctx.subscriptions.push( |
| vscode.commands.registerCommand('go.workspace.resetState', () => { |
| resetWorkspaceState(); |
| }) |
| ); |
| |
| ctx.subscriptions.push( |
| vscode.commands.registerCommand('go.global.resetState', () => { |
| resetGlobalState(); |
| }) |
| ); |
| |
| ctx.subscriptions.push( |
| vscode.commands.registerCommand('go.toggle.gc_details', () => { |
| if (!languageServerIsRunning) { |
| vscode.window.showErrorMessage( |
| '"Go: Toggle gc details" command is available only when the language server is running' |
| ); |
| return; |
| } |
| const doc = vscode.window.activeTextEditor?.document.uri.toString(); |
| if (!doc || !doc.endsWith('.go')) { |
| vscode.window.showErrorMessage('"Go: Toggle gc details" command cannot run when no Go file is open.'); |
| return; |
| } |
| vscode.commands.executeCommand('gc_details', doc).then(undefined, (reason0) => { |
| vscode.commands.executeCommand('gopls.gc_details', doc).then(undefined, (reason1) => { |
| vscode.window.showErrorMessage( |
| `"Go: Toggle gc details" command failed: gc_details:${reason0} gopls_gc_details:${reason1}` |
| ); |
| }); |
| }); |
| }) |
| ); |
| |
| ctx.subscriptions.push( |
| vscode.commands.registerCommand('go.apply.coverprofile', () => { |
| if (!vscode.window.activeTextEditor || !vscode.window.activeTextEditor.document.fileName.endsWith('.go')) { |
| vscode.window.showErrorMessage('Cannot apply coverage profile when no Go file is open.'); |
| return; |
| } |
| const lastCoverProfilePathKey = 'lastCoverProfilePathKey'; |
| const lastCoverProfilePath = getFromWorkspaceState(lastCoverProfilePathKey, ''); |
| vscode.window |
| .showInputBox({ |
| prompt: 'Enter the path to the coverage profile for current package', |
| value: lastCoverProfilePath |
| }) |
| .then((coverProfilePath) => { |
| if (!coverProfilePath) { |
| return; |
| } |
| if (!fileExists(coverProfilePath)) { |
| vscode.window.showErrorMessage(`Cannot find the file ${coverProfilePath}`); |
| return; |
| } |
| if (coverProfilePath !== lastCoverProfilePath) { |
| updateWorkspaceState(lastCoverProfilePathKey, coverProfilePath); |
| } |
| applyCodeCoverageToAllEditors( |
| coverProfilePath, |
| getWorkspaceFolderPath(vscode.window.activeTextEditor.document.uri) |
| ); |
| }); |
| }) |
| ); |
| |
| // Go Enviornment switching commands |
| ctx.subscriptions.push( |
| vscode.commands.registerCommand('go.environment.choose', () => { |
| chooseGoEnvironment(); |
| }) |
| ); |
| |
| // Survey related commands |
| ctx.subscriptions.push(vscode.commands.registerCommand('go.survey.showConfig', () => showSurveyConfig())); |
| ctx.subscriptions.push(vscode.commands.registerCommand('go.survey.resetConfig', () => resetSurveyConfig())); |
| |
| vscode.languages.setLanguageConfiguration(GO_MODE.language, { |
| wordPattern: /(-?\d*\.\d\w*)|([^`~!@#%^&*()\-=+[{\]}\\|;:'",.<>/?\s]+)/g |
| }); |
| |
| return extensionAPI; |
| } |
| |
| function showGoWelcomePage(ctx: vscode.ExtensionContext) { |
| // Update this list of versions when there is a new version where we want to |
| // show the welcome page on update. |
| const showVersions: string[] = ['0.27.0']; |
| // TODO(hyangah): use the content hash instead of hard-coded string. |
| // https://github.com/golang/vscode-go/issue/1179 |
| let goExtensionVersion = '0.27.0'; |
| let goExtensionVersionKey = 'go.extensionVersion'; |
| if (isInPreviewMode()) { |
| goExtensionVersion = '0.0.0'; |
| goExtensionVersionKey = 'go.nightlyExtensionVersion'; |
| } |
| |
| const savedGoExtensionVersion = getFromGlobalState(goExtensionVersionKey, ''); |
| |
| if (shouldShowGoWelcomePage(showVersions, goExtensionVersion, savedGoExtensionVersion)) { |
| WelcomePanel.createOrShow(ctx.extensionUri); |
| } |
| if (goExtensionVersion !== savedGoExtensionVersion) { |
| updateGlobalState(goExtensionVersionKey, goExtensionVersion); |
| } |
| } |
| |
| export function shouldShowGoWelcomePage(showVersions: string[], newVersion: string, oldVersion: string): boolean { |
| if (newVersion === oldVersion) { |
| return false; |
| } |
| const coercedNew = semver.coerce(newVersion); |
| const coercedOld = semver.coerce(oldVersion); |
| if (!coercedNew || !coercedOld) { |
| return true; |
| } |
| // Both semver.coerce(0.22.0) and semver.coerce(0.22.0-rc.1) will be 0.22.0. |
| return semver.gte(coercedNew, coercedOld) && showVersions.includes(coercedNew.toString()); |
| } |
| |
| async function showGoNightlyWelcomeMessage() { |
| const shown = getFromGlobalState(goNightlyPromptKey, false); |
| if (shown === true) { |
| return; |
| } |
| const prompt = async () => { |
| const selected = await vscode.window.showInformationMessage( |
| `Thank you for testing new features by using the Go Nightly extension! |
| We'd like to welcome you to share feedback and/or join our community of Go Nightly users and developers.`, |
| 'Share feedback', |
| 'Community resources' |
| ); |
| switch (selected) { |
| case 'Share feedback': |
| await vscode.env.openExternal( |
| vscode.Uri.parse('https://github.com/golang/vscode-go/blob/master/docs/nightly.md#feedback') |
| ); |
| break; |
| case 'Community resources': |
| await vscode.env.openExternal( |
| vscode.Uri.parse('https://github.com/golang/vscode-go/blob/master/docs/nightly.md#community') |
| ); |
| break; |
| default: |
| return; |
| } |
| // Only prompt again if the user clicked one of the buttons. |
| // They may want to look at the other option. |
| prompt(); |
| }; |
| prompt(); |
| |
| // Update state to indicate that we've shown this message to the user. |
| updateGlobalState(goNightlyPromptKey, true); |
| } |
| |
| const goNightlyPromptKey = 'goNightlyPrompt'; |
| |
| export function deactivate() { |
| return Promise.all([ |
| cancelRunningTests(), |
| Promise.resolve(cleanupTempDir()), |
| Promise.resolve(disposeGoStatusBar()) |
| ]); |
| } |
| |
| function runBuilds(document: vscode.TextDocument, goConfig: vscode.WorkspaceConfiguration) { |
| if (document.languageId !== 'go') { |
| return; |
| } |
| |
| buildDiagnosticCollection.clear(); |
| lintDiagnosticCollection.clear(); |
| vetDiagnosticCollection.clear(); |
| check(document.uri, goConfig) |
| .then((results) => { |
| results.forEach((result) => { |
| handleDiagnosticErrors(document, result.errors, result.diagnosticCollection); |
| }); |
| }) |
| .catch((err) => { |
| vscode.window.showInformationMessage('Error: ' + err); |
| }); |
| } |
| |
| function addOnSaveTextDocumentListeners(ctx: vscode.ExtensionContext) { |
| vscode.workspace.onDidSaveTextDocument(removeCodeCoverageOnFileSave, null, ctx.subscriptions); |
| vscode.workspace.onDidSaveTextDocument( |
| (document) => { |
| if (document.languageId !== 'go') { |
| return; |
| } |
| const session = vscode.debug.activeDebugSession; |
| if (session && session.type === 'go') { |
| const neverAgain = { title: "Don't Show Again" }; |
| const ignoreActiveDebugWarningKey = 'ignoreActiveDebugWarningKey'; |
| const ignoreActiveDebugWarning = getFromGlobalState(ignoreActiveDebugWarningKey); |
| if (!ignoreActiveDebugWarning) { |
| vscode.window |
| .showWarningMessage( |
| 'A debug session is currently active. Changes to your Go files may result in unexpected behaviour.', |
| neverAgain |
| ) |
| .then((result) => { |
| if (result === neverAgain) { |
| updateGlobalState(ignoreActiveDebugWarningKey, true); |
| } |
| }); |
| } |
| } |
| if (vscode.window.visibleTextEditors.some((e) => e.document.fileName === document.fileName)) { |
| runBuilds(document, getGoConfig(document.uri)); |
| } |
| }, |
| null, |
| ctx.subscriptions |
| ); |
| } |
| |
| function addOnChangeTextDocumentListeners(ctx: vscode.ExtensionContext) { |
| vscode.workspace.onDidChangeTextDocument(trackCodeCoverageRemovalOnFileChange, null, ctx.subscriptions); |
| vscode.workspace.onDidChangeTextDocument(removeTestStatus, null, ctx.subscriptions); |
| vscode.workspace.onDidChangeTextDocument(notifyIfGeneratedFile, ctx, ctx.subscriptions); |
| } |
| |
| function addOnChangeActiveTextEditorListeners(ctx: vscode.ExtensionContext) { |
| [updateGoStatusBar, applyCodeCoverage].forEach((listener) => { |
| // Call the listeners on initilization for current active text editor |
| if (vscode.window.activeTextEditor) { |
| listener(vscode.window.activeTextEditor); |
| } |
| vscode.window.onDidChangeActiveTextEditor(listener, null, ctx.subscriptions); |
| }); |
| } |
| |
| function checkToolExists(tool: string) { |
| if (tool === getBinPath(tool)) { |
| promptForMissingTool(tool); |
| } |
| } |
| |
| async function suggestUpdates(ctx: vscode.ExtensionContext) { |
| // TODO(hyangah): this is to clean up the unused key. Delete this code in 2021 Dec. |
| if (ctx.globalState.get('toolsGoInfo')) { |
| ctx.globalState.update('toolsGoInfo', null); |
| } |
| } |
| |
| function configureLanguageServer(ctx: vscode.ExtensionContext) { |
| // Subscribe to notifications for changes to the configuration |
| // of the language server, even if it's not currently in use. |
| ctx.subscriptions.push(vscode.workspace.onDidChangeConfiguration((e) => watchLanguageServerConfiguration(e))); |
| |
| // Set the function that is used to restart the language server. |
| // This is necessary, even if the language server is not currently |
| // in use. |
| restartLanguageServer = async () => { |
| startLanguageServerWithFallback(ctx, false); |
| }; |
| |
| // Start the language server, or fallback to the default language providers. |
| return startLanguageServerWithFallback(ctx, true); |
| } |
| |
| function getCurrentGoPathCommand() { |
| const gopath = getCurrentGoPath(); |
| let msg = `${gopath} is the current GOPATH.`; |
| const wasInfered = getGoConfig()['inferGopath']; |
| const root = getWorkspaceFolderPath(vscode.window.activeTextEditor && vscode.window.activeTextEditor.document.uri); |
| |
| // not only if it was configured, but if it was successful. |
| if (wasInfered && root && root.indexOf(gopath) === 0) { |
| const inferredFrom = vscode.window.activeTextEditor ? 'current folder' : 'workspace root'; |
| msg += ` It is inferred from ${inferredFrom}`; |
| } |
| |
| vscode.window.showInformationMessage(msg); |
| return gopath; |
| } |
| |
| async function getConfiguredGoToolsCommand() { |
| outputChannel.show(); |
| outputChannel.clear(); |
| outputChannel.appendLine('Checking configured tools....'); |
| // Tool's path search is done by getBinPathWithPreferredGopath |
| // which searches places in the following order |
| // 1) absolute path for the alternateTool |
| // 2) GOBIN |
| // 3) toolsGopath |
| // 4) gopath |
| // 5) GOROOT |
| // 6) PATH |
| outputChannel.appendLine('GOBIN: ' + process.env['GOBIN']); |
| outputChannel.appendLine('toolsGopath: ' + getToolsGopath()); |
| outputChannel.appendLine('gopath: ' + getCurrentGoPath()); |
| outputChannel.appendLine('GOROOT: ' + getCurrentGoRoot()); |
| const currentEnvPath = process.env['PATH'] || (process.platform === 'win32' ? process.env['Path'] : null); |
| outputChannel.appendLine('PATH: ' + currentEnvPath); |
| if (currentEnvPath !== envPath) { |
| outputChannel.appendLine(`PATH (vscode launched with): ${envPath}`); |
| } |
| outputChannel.appendLine(''); |
| |
| const goVersion = await getGoVersion(); |
| const allTools = getConfiguredTools(goVersion, getGoConfig(), getGoplsConfig()); |
| const goVersionTooOld = goVersion?.lt('1.12') || false; |
| |
| outputChannel.appendLine(`\tgo:\t${goVersion?.binaryPath}: ${goVersion?.version}`); |
| const toolsInfo = await Promise.all( |
| allTools.map(async (tool) => { |
| const toolPath = getBinPath(tool.name); |
| // TODO(hyangah): print alternate tool info if set. |
| if (!path.isAbsolute(toolPath)) { |
| // getBinPath returns the absolute path is the tool exists. |
| // (See getBinPathWithPreferredGopath which is called underneath) |
| return `\t${tool.name}:\tnot installed`; |
| } |
| if (goVersionTooOld) { |
| return `\t${tool.name}:\t${toolPath}: unknown version`; |
| } |
| try { |
| const out = await runGoVersionM(toolPath); |
| return `\t${tool.name}:${out.replace(/^/gm, '\t')}`; |
| } catch (e) { |
| return `\t${tool.name}:\t${toolPath}: go version -m failed: ${e}`; |
| } |
| }) |
| ); |
| toolsInfo.forEach((info) => { |
| outputChannel.appendLine(info); |
| }); |
| |
| let folders = vscode.workspace.workspaceFolders?.map((folder) => { |
| return { name: folder.name, path: folder.uri.fsPath }; |
| }); |
| if (!folders) { |
| folders = [{ name: 'no folder', path: undefined }]; |
| } |
| |
| outputChannel.appendLine(''); |
| outputChannel.appendLine('go env'); |
| for (const folder of folders) { |
| outputChannel.appendLine(`Workspace Folder (${folder.name}): ${folder.path}`); |
| try { |
| const out = await getGoEnv(folder.path); |
| // Append '\t' to the beginning of every line (^) of 'out'. |
| // 'g' = 'global matching', and 'm' = 'multi-line matching' |
| outputChannel.appendLine(out.replace(/^/gm, '\t')); |
| } catch (e) { |
| outputChannel.appendLine(`failed to run 'go env': ${e}`); |
| } |
| } |
| } |
| |
| function lintDiagnosticCollectionName(lintToolName: string) { |
| if (!lintToolName || lintToolName === 'golint') { |
| return 'go-lint'; |
| } |
| return `go-${lintToolName}`; |
| } |
| |
| // set GOROOT env var. If necessary, shows a warning. |
| export async function setGOROOTEnvVar(configGOROOT: string) { |
| if (!configGOROOT) { |
| return; |
| } |
| const goroot = configGOROOT ? resolvePath(configGOROOT) : undefined; |
| |
| const currentGOROOT = process.env['GOROOT']; |
| if (goroot === currentGOROOT) { |
| return; |
| } |
| if (!(await dirExists(goroot))) { |
| vscode.window.showWarningMessage(`go.goroot setting is ignored. ${goroot} is not a valid GOROOT directory.`); |
| return; |
| } |
| const neverAgain = { title: "Don't Show Again" }; |
| const ignoreGOROOTSettingWarningKey = 'ignoreGOROOTSettingWarning'; |
| const ignoreGOROOTSettingWarning = getFromGlobalState(ignoreGOROOTSettingWarningKey); |
| if (!ignoreGOROOTSettingWarning) { |
| vscode.window |
| .showInformationMessage( |
| `"go.goroot" setting (${goroot}) will be applied and set the GOROOT environment variable.`, |
| neverAgain |
| ) |
| .then((result) => { |
| if (result === neverAgain) { |
| updateGlobalState(ignoreGOROOTSettingWarningKey, true); |
| } |
| }); |
| } |
| |
| logVerbose(`setting GOROOT = ${goroot} (old value: ${currentGOROOT}) because "go.goroot": "${configGOROOT}"`); |
| if (goroot) { |
| process.env['GOROOT'] = goroot; |
| } else { |
| delete process.env.GOROOT; |
| } |
| } |