blob: 91c57b6c233bf42f5ee6327d321f792cdafbc186 [file] [log] [blame]
/* eslint-disable no-case-declarations */
/*---------------------------------------------------------
* 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 vscode = require('vscode');
import vscodeUri = require('vscode-uri');
import { getGoConfig } from './config';
import { formatGoVersion, GoEnvironmentOption, terminalCreationListener } from './goEnvironmentStatus';
import { GoDocumentSelector, isGoFile } from './goMode';
import { runGoEnv } from './goModules';
import { allToolsInformation } from './goToolsInformation';
import { getGoVersion } from './util';
import { GoExtensionContext } from './context';
import { CommandFactory } from './commands';
import { LanguageClient, State } from 'vscode-languageclient/node';
export const outputChannel = vscode.window.createOutputChannel('Go', { log: true });
const STATUS_BAR_ITEM_NAME = 'Go Diagnostics';
export const diagnosticsStatusBarItem = vscode.window.createStatusBarItem(
STATUS_BAR_ITEM_NAME,
vscode.StatusBarAlignment.Left
);
diagnosticsStatusBarItem.name = STATUS_BAR_ITEM_NAME;
// statusbar item for switching the Go environment
export let goEnvStatusbarItem: vscode.StatusBarItem;
let gomod: string;
let gowork: string;
const languageServerIcon = '$(zap)';
const languageServerErrorIcon = '$(warning)';
const languageServerStartingIcon = '$(sync~spin)';
export async function updateGoStatusBar(editor: vscode.TextEditor | undefined) {
if (!editor) {
return;
}
if (isGoFile(editor.document)) {
showGoStatusBar();
return;
}
if (editor.document.languageId?.toLowerCase() !== 'log') {
goEnvStatusbarItem.hide();
}
}
export const expandGoStatusBar: CommandFactory = (ctx, goCtx) => async () => {
// Only update the module path if we are in a Go file.
// This allows the user to open output windows without losing
// the go.mod information in the status bar.
const editor = vscode.window.activeTextEditor;
if (!!editor && isGoFile(editor.document)) {
const cwd = vscodeUri.Utils.dirname(editor.document.uri);
try {
const p = await runGoEnv(cwd, ['GOMOD', 'GOWORK']);
gomod = p['GOMOD'] === '/dev/null' || p['GOMOD'] === 'NUL' ? '' : p['GOMOD'];
gowork = p['GOWORK'];
} catch (e) {
outputChannel.debug(`failed to run go env from ${cwd} - ${e}`);
}
}
const { languageServerIsRunning, serverOutputChannel } = goCtx;
const options = [
{ label: 'Locate Configured Go Tools', description: 'display go env' },
{ label: 'Choose Go Environment' }
];
const cfg = goCtx.latestConfig;
// Get the gopls configuration.
const goConfig = getGoConfig();
const goplsIsRunning = languageServerIsRunning && cfg && cfg.serverName === 'gopls';
if (goplsIsRunning) {
const goplsVersion = cfg.version;
options.push({ label: `${languageServerIcon} Open 'gopls' trace`, description: `${goplsVersion?.version}` });
}
// In case gopls still need to be installed, cfg.serverName will be empty.
if (!goplsIsRunning && goConfig.get('useLanguageServer') === true && cfg?.serverName === '') {
options.push({
label: 'Install Go Language Server',
description: `${languageServerErrorIcon}'gopls' is required but missing`
});
}
// If modules is enabled, add link to mod file
if (gomod) {
options.push({ label: "Open 'go.mod'", description: gomod });
}
if (gowork) {
options.push({ label: "Open 'go.work'", description: gowork });
}
vscode.window.showQuickPick(options).then((item) => {
if (item) {
switch (item.label) {
case 'Locate Configured Go Tools':
vscode.commands.executeCommand('go.locate.tools');
break;
case 'Choose Go Environment':
vscode.commands.executeCommand('go.environment.choose');
break;
case `${languageServerIcon}Open 'gopls' trace`:
if (serverOutputChannel) {
serverOutputChannel.show();
}
break;
case 'Install Go Language Server':
vscode.commands.executeCommand('go.tools.install', [allToolsInformation['gopls']]);
break;
case "Open 'go.work'":
case "Open 'go.mod'":
if (item.description) {
const openPath = vscode.Uri.file(item.description);
vscode.workspace.openTextDocument(openPath).then((doc) => {
vscode.window.showTextDocument(doc);
});
break;
}
}
}
});
};
/**
* Initialize the status bar item with current Go binary
*/
export async function initGoStatusBar(goCtx: GoExtensionContext) {
const { languageClient } = goCtx;
if (!goEnvStatusbarItem) {
const STATUS_BAR_ITEM_NAME = 'Go';
goEnvStatusbarItem = vscode.window.createStatusBarItem(
STATUS_BAR_ITEM_NAME,
vscode.StatusBarAlignment.Right,
100.09999 // place the item right after the language status item https://github.com/microsoft/vscode-python/issues/18040#issuecomment-992567670.
);
goEnvStatusbarItem.name = STATUS_BAR_ITEM_NAME;
}
// set Go version and command
const version = await getGoVersion();
const goOption = new GoEnvironmentOption(version.binaryPath, formatGoVersion(version));
goEnvStatusbarItem.text = goOption.label;
goEnvStatusbarItem.command = 'go.environment.status';
// Add an icon to indicate that the 'gopls' server is running.
// Assume if it is configured it is already running, since the
// icon will be updated on an attempt to start.
const goConfig = getGoConfig();
updateLanguageServerIconGoStatusBar(languageClient, goConfig['useLanguageServer'] === true);
if (vscode.window.visibleTextEditors.some((editor) => !!editor && isGoFile(editor.document))) {
showGoStatusBar();
}
}
export function updateLanguageServerIconGoStatusBar(languageClient: LanguageClient | undefined, enabled: boolean) {
if (!goEnvStatusbarItem) {
return;
}
// Split the existing goEnvStatusbarItem.text into the version string part and
// the gopls icon part.
let text = goEnvStatusbarItem.text;
if (text.endsWith(languageServerIcon)) {
text = text.substring(0, text.length - languageServerIcon.length);
} else if (text.endsWith(languageServerErrorIcon)) {
text = text.substring(0, text.length - languageServerErrorIcon.length);
} else if (text.endsWith(languageServerStartingIcon)) {
text = text.substring(0, text.length - languageServerStartingIcon.length);
}
let color = undefined;
let icon = '';
if (!enabled || !languageClient) {
icon = '';
color = new vscode.ThemeColor('statusBarItem.warningBackground');
} else if (languageClient.state === State.Starting) {
icon = languageServerStartingIcon;
color = undefined;
} else if (languageClient.state === State.Running) {
icon = languageServerIcon;
color = undefined;
} else if (languageClient.state === State.Stopped) {
icon = languageServerErrorIcon;
color = new vscode.ThemeColor('statusBarItem.errorBackground');
}
goEnvStatusbarItem.text = text + icon;
goEnvStatusbarItem.backgroundColor = color;
}
/**
* disable the Go status bar items
*/
export function disposeGoStatusBar() {
if (goEnvStatusbarItem) {
goEnvStatusbarItem.dispose();
}
if (terminalCreationListener) {
terminalCreationListener.dispose();
}
for (const statusBarEntry of statusBarEntries) {
if (statusBarEntry) {
const [name, entry] = statusBarEntry;
statusBarEntries.delete(name);
entry.dispose();
}
}
}
/**
* Show the Go statusbar items on the statusbar
*/
export function showGoStatusBar() {
if (goEnvStatusbarItem) {
goEnvStatusbarItem.show();
}
}
// status bar item to show warning messages such as missing analysis tools.
const statusBarEntries = new Map<string, vscode.LanguageStatusItem>();
export function removeGoStatus(name: string) {
const statusBarEntry = statusBarEntries.get(name);
if (statusBarEntry) {
statusBarEntry.dispose();
statusBarEntries.delete(name);
}
}
export function addGoStatus(name: string): vscode.LanguageStatusItem {
let statusBarEntry = statusBarEntries.get(name);
if (!statusBarEntry) {
statusBarEntry = vscode.languages.createLanguageStatusItem(name, GoDocumentSelector);
statusBarEntries.set(name, statusBarEntry);
}
return statusBarEntry;
}