| /* eslint-disable @typescript-eslint/no-explicit-any */ |
| /*--------------------------------------------------------- |
| * Copyright (C) Microsoft Corporation. All rights reserved. |
| * Licensed under the MIT License. See LICENSE in the project root for license information. |
| *--------------------------------------------------------*/ |
| |
| 'use strict'; |
| |
| import * as moment from 'moment'; |
| import * as semver from 'semver'; |
| import * as vscode from 'vscode'; |
| import { getFormatTool } from './language/legacy/goFormat'; |
| import { allToolsInformation } from './goToolsInformation'; |
| import { GoVersion } from './util'; |
| |
| export interface Tool { |
| name: string; |
| importPath: string; |
| modulePath: string; |
| isImportant: boolean; |
| |
| /** |
| * Indicates whether this tool is replaced by gopls. |
| * |
| * This should be set to `true` ONLY for tools that are not directly |
| * exposed to the user as a configuration option. |
| * |
| * If a tool can be explicitly selected by the user through a setting (e.g. |
| * "go.formatTool"), this field must be `false`. In those cases, gopls is |
| * treated as an alternative choice, not a replacement, and the extension |
| * must honor the user's specific selection. |
| */ |
| replacedByGopls?: boolean; |
| description: string; |
| |
| // If true, consider prerelease version in prerelease mode |
| // (prerelease & dev) |
| usePrereleaseInPreviewMode?: boolean; |
| // If set, this string will be used when installing the tool |
| // instead of the default 'latest'. It can be used when |
| // we need to pin a tool version (`deadbeef`) or to use |
| // a dev version available in a branch (e.g. `master`). |
| defaultVersion?: string; |
| |
| // latestVersion and latestVersionTimestamp are hardcoded default values |
| // for the last known version of the given tool. |
| latestVersion?: semver.SemVer | null; |
| latestVersionTimestamp?: moment.Moment; |
| |
| // minimumGoVersion sets the minimum required version of Go |
| // for the tool. |
| minimumGoVersion?: semver.SemVer | null; |
| } |
| |
| /** |
| * ToolAtVersion is a Tool at a specific version. |
| * Lack of version implies the latest version. |
| */ |
| export interface ToolAtVersion extends Tool { |
| version?: semver.SemVer; |
| } |
| |
| export function getImportPathWithVersion( |
| tool: Tool, |
| version: semver.SemVer | string | undefined | null, |
| goVersion: GoVersion // This is the Go version to build the project. |
| ): string { |
| const importPath = tool.importPath; |
| if (version) { |
| if (version instanceof semver.SemVer) { |
| return importPath + '@v' + version; |
| } else { |
| return importPath + '@' + version; |
| } |
| } |
| if (tool.name === 'gopls') { |
| if (goVersion.lt('1.19')) return importPath + '@v0.14.2'; |
| if (goVersion.lt('1.21')) return importPath + '@v0.15.3'; |
| } |
| if (tool.name === 'dlv') { |
| if (goVersion.lt('1.19')) return importPath + '@v1.20.2'; |
| if (goVersion.lt('1.21')) return importPath + '@v1.22.1'; |
| } |
| if (tool.name === 'staticcheck') { |
| if (goVersion.lt('1.19')) return importPath + '@v0.3.3'; |
| if (goVersion.lt('1.21')) return importPath + '@v0.4.7'; |
| } |
| if (tool.name === 'gofumpt') { |
| if (goVersion.lt('1.19')) return importPath + '@v0.4.0'; // pre-go1.19 |
| if (goVersion.lt('1.20')) return importPath + '@v0.5.0'; // go1.19 |
| if (goVersion.lt('1.22')) return importPath + '@v0.6.0'; // go1.20~1.21 |
| } |
| if (tool.name === 'golangci-lint') { |
| if (goVersion.lt('1.20')) return importPath + '@v1.53.3'; |
| if (goVersion.lt('1.21')) return importPath + '@v1.55.2'; |
| } |
| if (tool.defaultVersion) { |
| return importPath + '@' + tool.defaultVersion; |
| } |
| return importPath + '@latest'; |
| } |
| |
| export function containsTool(tools: Tool[], tool: Tool): boolean { |
| return tools.indexOf(tool) > -1; |
| } |
| |
| export function containsString(tools: Tool[], toolName: string): boolean { |
| return tools.some((tool) => tool.name === toolName); |
| } |
| |
| export function getTool(name: string): Tool | undefined { |
| const [n] = name.split('@'); |
| return allToolsInformation.get(n); |
| } |
| |
| export function getToolAtVersion(name: string, version?: semver.SemVer): ToolAtVersion { |
| return { ...allToolsInformation.get(name)!, version }; |
| } |
| |
| // hasModSuffix returns true if the given tool has a different, module-specific |
| // name to avoid conflicts. |
| export function hasModSuffix(tool: Tool): boolean { |
| return tool.name.endsWith('-gomod'); |
| } |
| |
| /** |
| * getRequiredTools returns the tools that required explicitly by the user |
| * through settings like "go.lintTool", "go.formatTool" and implicitly by the |
| * extension's functionality like "goplay". |
| * |
| * Tools that is replaced by gopls will not be returned if gopls is enabled. |
| */ |
| export function getRequiredTools(goConfig: vscode.WorkspaceConfiguration): Tool[] { |
| // If language server is enabled, don't suggest tools that are replaced by gopls. |
| // TODO(github.com/golang/vscode-go/issues/388): decide what to do when |
| // the go version is no longer supported by gopls while the legacy tools are |
| // no longer working (or we remove the legacy language feature providers completely). |
| const useLanguageServer = goConfig.get<boolean>('useLanguageServer'); |
| |
| // The default tools that is required by the extension's functionality. |
| const toolNames = new Set<string>(['gotests', 'gomodifytags', 'impl', 'goplay']); |
| |
| // Check if the system supports dlv, i.e. is 64-bit. |
| // There doesn't seem to be a good way to check if the mips and s390 |
| // families are 64-bit, so just try to install it and hope for the best. |
| if (process.arch.match(/^(mips|mipsel|ppc64|s390|s390x|x64|arm64)$/)) { |
| toolNames.add('dlv'); |
| } |
| |
| // Configurable linter and formatter through "go.formatTool" and "go.lintTool". |
| toolNames.add(getFormatTool(goConfig)); |
| const lintToolName = goConfig.get<string>('lintTool'); |
| if (lintToolName) { |
| toolNames.add(lintToolName); |
| } |
| |
| // Add the language server if the user has chosen to do so. |
| if (useLanguageServer) { |
| toolNames.add('gopls'); |
| } |
| |
| const tools: Tool[] = []; |
| for (const name of toolNames) { |
| const tool = allToolsInformation.get(name); |
| if (!tool) { |
| continue; |
| } |
| // If language server is enabled, filter out tools that are replaced by gopls. |
| if (useLanguageServer && tool.replacedByGopls) { |
| continue; |
| } |
| tools.push(tool); |
| } |
| return tools; |
| } |