diff --git a/src/goDeclaration.ts b/src/goDeclaration.ts index 7ead46f16..b79f5609d 100644 --- a/src/goDeclaration.ts +++ b/src/goDeclaration.ts @@ -11,7 +11,7 @@ import path = require('path'); import { byteOffsetAt, getBinPath, runGodoc, getWorkspaceFolderPath } from './util'; import { promptForMissingTool, promptForUpdatingTool } from './goInstallTools'; import { getGoVersion, SemVersion, goKeywords, isPositionInString, getToolsEnvVars, getFileArchive, killProcess } from './util'; -import { isModSupported, promptToUpdateToolForModules } from './goModules'; +import { promptToUpdateToolForModules, getModPath } from './goModules'; const missingToolMsg = 'Missing tool: '; @@ -31,6 +31,7 @@ interface GoDefinitionInput { word: string; includeDocs: boolean; isMod: boolean; + modPath: string; } export function definitionLocation(document: vscode.TextDocument, position: vscode.Position, goConfig: vscode.WorkspaceConfiguration, includeDocs: boolean, token: vscode.CancellationToken): Promise { @@ -46,13 +47,14 @@ export function definitionLocation(document: vscode.TextDocument, position: vsco } let toolForDocs = goConfig['docsTool'] || 'godoc'; return getGoVersion().then((ver: SemVersion) => { - return isModSupported(document.uri).then(isMod => { + return getModPath(document.uri).then(modPath => { const input: GoDefinitionInput = { document, position, word, includeDocs, - isMod + isMod: modPath !== '', + modPath, }; if (toolForDocs === 'godoc' || (ver && (ver.major < 1 || (ver.major === 1 && ver.minor < 6)))) { return definitionLocation_godef(input, token); @@ -91,7 +93,13 @@ function definitionLocation_godef(input: GoDefinitionInput, token: vscode.Cancel if (token) { token.onCancellationRequested(() => killProcess(p)); } - const cwd = getWorkspaceFolderPath(input.document.uri); + + let cwd: string; + if (input.isMod) { + cwd = input.modPath; + } else { + cwd = getWorkspaceFolderPath(input.document.uri); + } return new Promise((resolve, reject) => { // Spawn `godef` process diff --git a/src/goModules.ts b/src/goModules.ts index 6b7d4c16c..1fba4d0a2 100644 --- a/src/goModules.ts +++ b/src/goModules.ts @@ -1,68 +1,72 @@ -import { getBinPath, getGoVersion, sendTelemetryEvent, getToolsEnvVars, getCurrentGoPath } from './util'; +import { getBinPath, getGoVersion, sendTelemetryEvent, getToolsEnvVars } from './util'; import path = require('path'); import cp = require('child_process'); import vscode = require('vscode'); import { getFromGlobalState, updateGlobalState } from './stateUtils'; import { installTools } from './goInstallTools'; -function containsModFile(folderPath: string): Promise { - let goExecutable = getBinPath('go'); - if (!goExecutable) { - return Promise.reject(new Error('Cannot find "go" binary. Update PATH or GOROOT appropriately.')); - } - return new Promise(resolve => { - cp.execFile(goExecutable, ['env', 'GOMOD'], { cwd: folderPath, env: getToolsEnvVars() }, (err, stdout) => { - if (err) { - console.warn(`Error when running go env GOMOD: ${err}`); - return resolve(false); - } - let [goMod] = stdout.split('\n'); - resolve(!!goMod); - }); - }); -} const workspaceModCache = new Map(); -const packageModCache = new Map(); +const folderModCache = new Map(); export function isModSupported(fileuri: vscode.Uri): Promise { + return getModPath(fileuri).then(modPath => { + return modPath !== ''; + }); +} + +export function getModPath(fileuri: vscode.Uri): Promise { + const folderPath = path.dirname(fileuri.fsPath); + + const hit = folderModCache.get(folderPath); + if (hit !== undefined) { + return Promise.resolve(hit); + } + + for (let k of Array.from(workspaceModCache.keys())) { + if (folderPath.startsWith(k)) { + folderModCache.set(folderPath, k); + return Promise.resolve(k); + } + } + return getGoVersion().then(value => { if (value && (value.major !== 1 || value.minor < 11)) { - return false; + folderModCache.set(folderPath, ''); + return ''; } - const workspaceFolder = vscode.workspace.getWorkspaceFolder(fileuri); - if (workspaceFolder && workspaceModCache.get(workspaceFolder.uri.fsPath)) { - return true; - } - const pkgPath = path.dirname(fileuri.fsPath); - if (packageModCache.get(pkgPath)) { - if (workspaceFolder && pkgPath === workspaceFolder.uri.fsPath) { - workspaceModCache.set(workspaceFolder.uri.fsPath, true); - logModuleUsage(true); - } else { - logModuleUsage(false); + return new Promise(resolve => { + let goExecutable = getBinPath('go'); + if (!goExecutable) { + return Promise.reject(new Error('Cannot find "go" binary. Update PATH or GOROOT appropriately.')); } - return true; - } - return containsModFile(pkgPath).then(result => { - packageModCache.set(pkgPath, result); - if (result) { + cp.execFile(goExecutable, ['list', '-m', '-f', '{{.GoMod}}'], { cwd: folderPath, env: getToolsEnvVars() }, (err, stdout) => { + if (err) { + resolve(''); + } + let [goMod] = stdout.split('\n'); + resolve(goMod); + }); + }).then(result => { + let modPath: string; + if (result !== '') { const goConfig = vscode.workspace.getConfiguration('go', fileuri); if (goConfig['inferGopath'] === true) { goConfig.update('inferGopath', false, vscode.ConfigurationTarget.WorkspaceFolder); alertDisablingInferGopath(); } + logModuleUsage(true); + modPath = path.dirname(result); + workspaceModCache.set(modPath, true); } else { - let currentGopath = getCurrentGoPath(); - if (currentGopath) { - currentGopath = currentGopath.split(path.delimiter)[0]; - if (fileuri.fsPath.startsWith(path.join(currentGopath, 'pkg', 'mod'))) { - return true; - } - } + modPath = ''; } - return result; + + folderModCache.set(folderPath, modPath); + return modPath; }); + }).catch(() => { + return ''; }); } @@ -70,25 +74,10 @@ export function updateWorkspaceModCache() { if (!vscode.workspace.workspaceFolders) { return; } - let inferGopathUpdated = false; const promises = vscode.workspace.workspaceFolders.map(folder => { - return containsModFile(folder.uri.fsPath).then(result => { - workspaceModCache.set(folder.uri.fsPath, result); - if (result) { - logModuleUsage(true); - const goConfig = vscode.workspace.getConfiguration('go', folder.uri); - if (goConfig['inferGopath'] === true) { - return goConfig.update('inferGopath', false, vscode.ConfigurationTarget.WorkspaceFolder) - .then(() => inferGopathUpdated = true); - } - } - }); - }); - Promise.all(promises).then(() => { - if (inferGopathUpdated) { - alertDisablingInferGopath(); - } + return getModPath(folder.uri); }); + Promise.all(promises); } function alertDisablingInferGopath() {