blob: b97e018e97a43d792373e4ffde3f4c1dea72074e [file] [log] [blame]
/*---------------------------------------------------------
* Copyright (C) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See LICENSE in the project root for license information.
*--------------------------------------------------------*/
'use strict';
import cp = require('child_process');
import vscode = require('vscode');
import { toolExecutionEnvironment } from './goEnv';
import { promptForMissingTool, promptForUpdatingTool } from './goInstallTools';
import { getCurrentGoRoot } from './goPath';
import { getBinPath, getGoConfig, getWorkspaceFolderPath, killTree } from './util';
// Keep in sync with github.com/acroca/go-symbols'
interface GoSymbolDeclaration {
name: string;
kind: string;
package: string;
path: string;
line: number;
character: number;
}
export class GoWorkspaceSymbolProvider implements vscode.WorkspaceSymbolProvider {
private goKindToCodeKind: { [key: string]: vscode.SymbolKind } = {
package: vscode.SymbolKind.Package,
import: vscode.SymbolKind.Namespace,
var: vscode.SymbolKind.Variable,
type: vscode.SymbolKind.Interface,
func: vscode.SymbolKind.Function,
const: vscode.SymbolKind.Constant
};
public provideWorkspaceSymbols(
query: string,
token: vscode.CancellationToken
): Thenable<vscode.SymbolInformation[]> {
const convertToCodeSymbols = (decls: GoSymbolDeclaration[], symbols: vscode.SymbolInformation[]): void => {
if (!decls) {
return;
}
for (const decl of decls) {
let kind: vscode.SymbolKind;
if (decl.kind !== '') {
kind = this.goKindToCodeKind[decl.kind];
}
const pos = new vscode.Position(decl.line, decl.character);
const symbolInfo = new vscode.SymbolInformation(
decl.name,
kind,
new vscode.Range(pos, pos),
vscode.Uri.file(decl.path),
''
);
symbols.push(symbolInfo);
}
};
const root = getWorkspaceFolderPath(
vscode.window.activeTextEditor && vscode.window.activeTextEditor.document.uri
);
const goConfig = getGoConfig();
if (!root && !goConfig.gotoSymbol.includeGoroot) {
vscode.window.showInformationMessage('No workspace is open to find symbols.');
return;
}
return getWorkspaceSymbols(root, query, token, goConfig).then((results) => {
const symbols: vscode.SymbolInformation[] = [];
convertToCodeSymbols(results, symbols);
return symbols;
});
}
}
export function getWorkspaceSymbols(
workspacePath: string,
query: string,
token: vscode.CancellationToken,
goConfig?: vscode.WorkspaceConfiguration,
ignoreFolderFeatureOn: boolean = true
): Thenable<GoSymbolDeclaration[]> {
if (!goConfig) {
goConfig = getGoConfig();
}
const gotoSymbolConfig = goConfig['gotoSymbol'];
const calls: Promise<GoSymbolDeclaration[]>[] = [];
const ignoreFolders: string[] = gotoSymbolConfig ? gotoSymbolConfig['ignoreFolders'] : [];
const baseArgs =
ignoreFolderFeatureOn && ignoreFolders && ignoreFolders.length > 0 ? ['-ignore', ignoreFolders.join(',')] : [];
calls.push(callGoSymbols([...baseArgs, workspacePath, query], token));
if (gotoSymbolConfig.includeGoroot) {
const goRoot = getCurrentGoRoot();
const gorootCall = callGoSymbols([...baseArgs, goRoot, query], token);
calls.push(gorootCall);
}
return Promise.all(calls)
.then(([...results]) => <GoSymbolDeclaration[]>[].concat(...results))
.catch((err: Error) => {
if (err && (<any>err).code === 'ENOENT') {
promptForMissingTool('go-symbols');
}
if (err.message.startsWith('flag provided but not defined: -ignore')) {
promptForUpdatingTool('go-symbols');
return getWorkspaceSymbols(workspacePath, query, token, goConfig, false);
}
});
}
function callGoSymbols(args: string[], token: vscode.CancellationToken): Promise<GoSymbolDeclaration[]> {
const gosyms = getBinPath('go-symbols');
const env = toolExecutionEnvironment();
let p: cp.ChildProcess;
if (token) {
token.onCancellationRequested(() => killTree(p.pid));
}
return new Promise((resolve, reject) => {
p = cp.execFile(gosyms, args, { maxBuffer: 1024 * 1024, env }, (err, stdout, stderr) => {
if (err && stderr && stderr.startsWith('flag provided but not defined: -ignore')) {
return reject(new Error(stderr));
} else if (err) {
return reject(err);
}
const result = stdout.toString();
const decls = <GoSymbolDeclaration[]>JSON.parse(result);
return resolve(decls);
});
});
}