diff --git a/src/goMain.ts b/src/goMain.ts index 9218a0a8f..753623f83 100644 --- a/src/goMain.ts +++ b/src/goMain.ts @@ -11,6 +11,7 @@ import { GoHoverProvider } from './goExtraInfo'; import { GoDefinitionProvider } from './goDeclaration'; import { GoReferenceProvider } from './goReferences'; import { GoImplementationProvider } from './goImplementations'; +import { GoTypeDefinitionProvider } from './goTypeDefinition'; import { GoDocumentFormattingEditProvider } from './goFormat'; import { GoRenameProvider } from './goRename'; import { GoDocumentSymbolProvider } from './goOutline'; @@ -180,6 +181,7 @@ export function activate(ctx: vscode.ExtensionContext): void { ctx.subscriptions.push(vscode.languages.registerCodeActionsProvider(GO_MODE, new GoCodeActionProvider())); ctx.subscriptions.push(vscode.languages.registerCodeLensProvider(GO_MODE, testCodeLensProvider)); ctx.subscriptions.push(vscode.languages.registerCodeLensProvider(GO_MODE, referencesCodeLensProvider)); + ctx.subscriptions.push(vscode.languages.registerTypeDefinitionProvider(GO_MODE, new GoTypeDefinitionProvider())); ctx.subscriptions.push(vscode.debug.registerDebugConfigurationProvider('go', new GoDebugConfigurationProvider())); buildDiagnosticCollection = vscode.languages.createDiagnosticCollection('go'); diff --git a/src/goTypeDefinition.ts b/src/goTypeDefinition.ts new file mode 100644 index 000000000..ada8dea72 --- /dev/null +++ b/src/goTypeDefinition.ts @@ -0,0 +1,95 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------*/ + +'use strict'; + +import vscode = require('vscode'); +import cp = require('child_process'); +import path = require('path'); +import { byteOffsetAt, getBinPath, canonicalizeGOPATHPrefix, getFileArchive, killTree } from './util'; +import { promptForMissingTool, promptForUpdatingTool } from './goInstallTools'; +import { getToolsEnvVars } from './util'; + +interface GuruDescribeOutput { + desc: string; + pos: string; + detail: string; + value: GuruDescribeValueOutput; +} + +interface GuruDescribeValueOutput { + type: string; + value: string; + objpos: string; + typespos: GuruDefinitionOutput[]; +} + +interface GuruDefinitionOutput { + objpos: string; + desc: string; +} + +export class GoTypeDefinitionProvider implements vscode.TypeDefinitionProvider { + provideTypeDefinition(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): vscode.ProviderResult { + return new Promise((resolve, reject) => { + let goGuru = getBinPath('guru'); + if (!path.isAbsolute(goGuru)) { + promptForMissingTool('guru'); + return reject('Cannot find tool "guru" to find type definitions.'); + } + + let filename = canonicalizeGOPATHPrefix(document.fileName); + let offset = byteOffsetAt(document, position); + let env = getToolsEnvVars(); + let buildTags = vscode.workspace.getConfiguration('go', document.uri)['buildTags']; + let args = buildTags ? ['-tags', buildTags] : []; + args.push('-json', '-modified', 'describe', `${filename}:#${offset.toString()}`); + + let process = cp.execFile(goGuru, args, { env }, (err, stdout, stderr) => { + try { + if (err && (err).code === 'ENOENT') { + promptForMissingTool('guru'); + return resolve(null); + } + + if (err) { + return reject(err); + } + + let guruOutput = JSON.parse(stdout.toString()); + let results: vscode.Location[] = []; + + if (!guruOutput.value || !guruOutput.value.typespos) { + if (guruOutput.value && guruOutput.value.type) { + promptForUpdatingTool('guru'); + } + return resolve(null); + } + + guruOutput.value.typespos.forEach(ref => { + let match = /^(.*):(\d+):(\d+)/.exec(ref.objpos); + if (!match) { + return; + } + let [_, file, line, col] = match; + let referenceResource = vscode.Uri.file(file); + let pos = new vscode.Position(parseInt(line) - 1, parseInt(col) - 1); + results.push(new vscode.Location(referenceResource, pos)); + }); + + resolve(results); + } catch (e) { + reject(e); + } + }); + if (process.pid) { + process.stdin.end(getFileArchive(document)); + } + token.onCancellationRequested(() => + killTree(process.pid) + ); + }); + } +} \ No newline at end of file diff --git a/test/go.test.ts b/test/go.test.ts index fafe691c1..2ab3bf678 100644 --- a/test/go.test.ts +++ b/test/go.test.ts @@ -105,6 +105,7 @@ suite('Go Extension Tests', () => { return vscode.workspace.openTextDocument(uri).then((textDocument) => { let promises = testCases.map(([position, expected, expectedDoc, expectedParams]) => provider.provideSignatureHelp(textDocument, position, null).then(sigHelp => { + assert.ok(sigHelp, `No signature for gogetdocTestData/test.go:${position}`); assert.equal(sigHelp.signatures.length, 1, 'unexpected number of overloads'); assert.equal(sigHelp.signatures[0].label, expected); assert.equal(sigHelp.signatures[0].documentation, expectedDoc);