blob: a1650a282403f9131a61e666bedce27eb08ec9ee [file] [log] [blame]
/*---------------------------------------------------------
* 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 fs = require('fs-extra');
import os = require('os');
import path = require('path');
import vscode = require('vscode');
import { updateGoVarsFromConfig } from './goInstallTools';
import { getCurrentGoRoot } from './goPath';
import { getFromWorkspaceState, updateWorkspaceState } from './stateUtils';
import { getGoVersion } from './util';
interface GoEnvironmentOption {
path: string;
label: string;
}
// statusbar item for switching the Go environment
let goEnvStatusbarItem: vscode.StatusBarItem;
/**
* Initialize the status bar item with current Go binary
*/
export async function initGoStatusBar() {
if (!goEnvStatusbarItem) {
goEnvStatusbarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 50);
}
// set Go version and command
const version = await getGoVersion();
hideGoStatusBar();
goEnvStatusbarItem.text = formatGoVersion(version.format());
goEnvStatusbarItem.command = 'go.environment.choose';
showGoStatusBar();
}
/**
* disable the Go environment status bar item
*/
export function disposeGoStatusBar() {
if (!!goEnvStatusbarItem) {
goEnvStatusbarItem.dispose();
}
}
/**
* Show the Go Environment statusbar item on the statusbar
*/
export function showGoStatusBar() {
if (!!goEnvStatusbarItem) {
goEnvStatusbarItem.show();
}
}
/**
* Hide the Go Environment statusbar item from the statusbar
*/
export function hideGoStatusBar() {
if (!!goEnvStatusbarItem) {
goEnvStatusbarItem.hide();
}
}
/**
* Present a command palette menu to the user to select their go binary
*/
export async function chooseGoEnvironment() {
if (!goEnvStatusbarItem) {
return;
}
// get list of Go versions
const sdkPath = path.join(os.homedir(), 'sdk');
if (!await fs.pathExists(sdkPath)) {
vscode.window.showErrorMessage(`SDK path does not exist: ${sdkPath}`);
return;
}
const subdirs = await fs.readdir(sdkPath);
// create quick pick items
// the dir happens to be the version, which will be used as the label
// the path is assembled and used as the description
const goSdkOptions: vscode.QuickPickItem[] = subdirs.map((dir: string) => {
return {
label: dir.replace('go', 'Go '),
description: path.join(sdkPath, dir, 'bin', 'go'),
};
});
// get default option
let defaultOption: vscode.QuickPickItem;
try {
const defaultGo = await getDefaultGoOption();
defaultOption = {
label: defaultGo.label,
description: defaultGo.path
};
} catch (err) {
vscode.window.showErrorMessage(err.message);
return;
}
// dedup options by eliminating duplicate paths (description)
const options = [defaultOption, ...goSdkOptions].reduce((opts, nextOption) => {
if (opts.find((op) => op.description === nextOption.description)) {
return opts;
}
return [...opts, nextOption];
}, [] as vscode.QuickPickItem[]);
// show quick pick to select new go version
const { label, description } = await vscode.window.showQuickPick<vscode.QuickPickItem>(options);
vscode.window.showInformationMessage(`Current GOROOT: ${description}`);
// update currently selected go
await setSelectedGo({
label,
path: description,
});
}
/**
* update the selected go path and label in the workspace state
*/
async function setSelectedGo(selectedGo: GoEnvironmentOption) {
// the go-environment state should follow the below format
// TODO: restart language server when the Go binary is switched
// TODO: determine if changes to settings.json need to be made
await updateWorkspaceState('selected-go', selectedGo);
goEnvStatusbarItem.text = selectedGo.label;
}
/**
* retreive the current selected Go from the workspace state
*/
export async function getSelectedGo(): Promise<GoEnvironmentOption> {
return await getFromWorkspaceState('selected-go');
}
/**
* return reference to the statusbar item
*/
export function getGoEnvironmentStatusbarItem(): vscode.StatusBarItem {
return goEnvStatusbarItem;
}
export async function getActiveGoRoot(): Promise<string | undefined> {
// look for current current go binary
let goroot = getCurrentGoRoot();
if (!goroot) {
await updateGoVarsFromConfig();
goroot = getCurrentGoRoot();
}
return goroot || undefined;
}
export function formatGoVersion(version: string): string {
const versionWords = version.split(' ');
if (versionWords[0] === 'devel') {
// Go devel +hash
return `Go ${versionWords[0]} ${versionWords[4]}`;
} else if (versionWords.length > 0) {
// some other version format
return `Go ${version.substr(0, 8)}`;
} else {
// default semantic version format
return `Go ${versionWords[0]}`;
}
}
export async function getDefaultGoOption(): Promise<GoEnvironmentOption> {
// make goroot default to go.goroot
const goroot = await getActiveGoRoot();
if (!goroot) {
throw new Error('No Go command could be found.');
}
// set Go version and command
const version = await getGoVersion();
return {
path: path.join(goroot, 'bin', 'go'),
label: formatGoVersion(version.format()),
};
}