Skip to content
This repository has been archived by the owner on Jul 15, 2023. It is now read-only.

optimize godef-gomod cache, better support one workspace for multi go… #2216

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions src/goDeclaration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: ';

Expand All @@ -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<GoDefinitionInformation> {
Expand All @@ -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);
Expand Down Expand Up @@ -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<GoDefinitionInformation>((resolve, reject) => {
// Spawn `godef` process
Expand Down
109 changes: 49 additions & 60 deletions src/goModules.ts
Original file line number Diff line number Diff line change
@@ -1,94 +1,83 @@
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<boolean> {
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<string, boolean>();
const packageModCache = new Map<string, boolean>();
const folderModCache = new Map<string, string>();

export function isModSupported(fileuri: vscode.Uri): Promise<boolean> {
return getModPath(fileuri).then(modPath => {
return modPath !== '';
});
}

export function getModPath(fileuri: vscode.Uri): Promise<string> {
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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This return applies just to the callback passed to the forEach call. If you want to exit the getModPath function when you folderPath.startsWith(k) is true, then you need to use a simple for loop

}
}

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<string>(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 '';
});
}

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() {
Expand Down