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

Commit

Permalink
Support both godef and gogetdoc
Browse files Browse the repository at this point in the history
  • Loading branch information
ramya-rao-a committed Nov 17, 2016
1 parent c5268dc commit 0020119
Show file tree
Hide file tree
Showing 6 changed files with 178 additions and 51 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ install:
- npm install
- npm run vscode:prepublish
- go get -u -v github.com/nsf/gocode
- go get -u -v github.com/zmb3/gogetdoc
- if [[ "$(go version)" =~ "go version go1.5" ]]; then go get -u -v github.com/rogpeppe/godef; else go get -u -v github.com/zmb3/gogetdoc; fi
- if [[ "$(go version)" =~ "go version go1.5" ]]; then echo cannot get golint; else go get -u -v github.com/golang/lint/golint; fi
- go get -u -v github.com/lukehoban/go-outline
- go get -u -v sourcegraph.com/sqs/goreturns
Expand Down
23 changes: 21 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,12 @@
"default": "."
},
"mode": {
"enum": ["debug", "remote", "test", "exec"],
"enum": [
"debug",
"remote",
"test",
"exec"
],
"description": "One of 'debug', 'remote', 'test', 'exec'.",
"default": "debug"
},
Expand Down Expand Up @@ -295,7 +300,12 @@
"go.formatTool": {
"type": "string",
"default": "goreturns",
"description": "Pick 'gofmt', 'goimports' or 'goreturns' to run on format."
"description": "Pick 'gofmt', 'goimports' or 'goreturns' to run on format.",
"enum": [
"gofmt",
"goimports",
"goreturns"
]
},
"go.formatFlags": {
"type": "array",
Expand Down Expand Up @@ -365,6 +375,15 @@
"type": "boolean",
"default": true,
"description": "Autocomplete members from unimported packages."
},
"go.docsTool": {
"type": "string",
"default": "godoc",
"description": "Pick 'godoc' or 'gogetdoc' to get documentation. In Go 1.5, godoc is used regardless of the choice here.",
"enum": [
"godoc",
"gogetdoc"
]
}
}
}
Expand Down
119 changes: 103 additions & 16 deletions src/goDeclaration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,49 +7,136 @@

import vscode = require('vscode');
import cp = require('child_process');
import path = require('path');
import { getBinPath } from './goPath';
import { byteOffsetAt } from './util';
import { promptForMissingTool } from './goInstallTools';
import { getGoVersion, SemVersion } from './util';

export interface GoDefinitionInformtation {
file: string;
line: number;
column: number;
docInfo: GoDocInfomation;
doc: string;
declarationlines: string[];
toolUsed: string;
}

export function definitionLocation(document: vscode.TextDocument, position: vscode.Position, includeDocs = true): Promise<GoDefinitionInformtation> {
let toolToUse = vscode.workspace.getConfiguration('go')['docsTool'];
return getGoVersion().then((ver: SemVersion) => {
if (!ver) {
return Promise.resolve(null);
}
if (toolToUse === 'godoc' || ver.major < 1 || (ver.major === 1 && ver.minor < 6)) {
return definitionLocation_godef(document, position, includeDocs);
}
return definitionLocation_gogetdoc(document, position);
});
}

function definitionLocation_godef(document: vscode.TextDocument, position: vscode.Position, includeDocs = true): Promise<GoDefinitionInformtation> {
return new Promise<GoDefinitionInformtation>((resolve, reject) => {

let wordAtPosition = document.getWordRangeAtPosition(position);
let offset = byteOffsetAt(document, position);
let gogetdoc = getBinPath('gogetdoc');
let p = cp.execFile(gogetdoc, ['-u', '-json', '-modified', '-pos', document.fileName + ':#' + offset.toString()], {}, (err, stdout, stderr) => {

let godef = getBinPath('godef');

// Spawn `godef` process
let p = cp.execFile(godef, ['-t', '-i', '-f', document.fileName, '-o', offset.toString()], {}, (err, stdout, stderr) => {
try {
if (err && (<any>err).code === 'ENOENT') {
promptForMissingTool('gogetdoc');
promptForMissingTool('godef');
}
if (err) return resolve(null);
let goDocInfomation = <GoDocInfomation>JSON.parse(stdout.toString());
let match = /(.*):(\d+):(\d+)/.exec(goDocInfomation.pos);
let result = stdout.toString();
let lines = result.split('\n');
let match = /(.*):(\d+):(\d+)/.exec(lines[0]);
if (!match) {
return resolve({
file: null,
line: 0,
column: 0,
docInfo: goDocInfomation
});
// TODO: Gotodef on pkg name:
// /usr/local/go/src/html/template\n
return resolve(null);
}
let [_, file, line, col] = match;
return resolve({
let signature = lines[1];
let godoc = getBinPath('godoc');
let pkgPath = path.dirname(file);
let definitionInformation: GoDefinitionInformtation = {
file: file,
line: +line - 1,
column: +col - 1,
docInfo: goDocInfomation
column: + col - 1,
declarationlines: lines.splice(1),
toolUsed: 'godef',
doc: null
};
if (!includeDocs) {
return resolve(definitionInformation);
}
cp.execFile(godoc, [pkgPath], {}, (err, stdout, stderr) => {
if (err && (<any>err).code === 'ENOENT') {
vscode.window.showInformationMessage('The "godoc" command is not available.');
}
let godocLines = stdout.toString().split('\n');
let doc = '';
let sigName = signature.substring(0, signature.indexOf(' '));
let sigParams = signature.substring(signature.indexOf(' func') + 5);
let searchSignature = 'func ' + sigName + sigParams;
for (let i = 0; i < godocLines.length; i++) {
if (godocLines[i] === searchSignature) {
while (godocLines[++i].startsWith(' ')) {
doc += godocLines[i].substring(4) + '\n';
}
break;
}
}
if (doc !== '') {
definitionInformation.doc = doc;
}
return resolve(definitionInformation);
});
} catch (e) {
reject(e);
}
});
p.stdin.end(document.getText());
});
}

function definitionLocation_gogetdoc(document: vscode.TextDocument, position: vscode.Position): Promise<GoDefinitionInformtation> {
return new Promise<GoDefinitionInformtation>((resolve, reject) => {
let wordAtPosition = document.getWordRangeAtPosition(position);
let offset = byteOffsetAt(document, position);
let gogetdoc = getBinPath('gogetdoc');
let p = cp.execFile(gogetdoc, ['-u', '-json', '-modified', '-pos', document.fileName + ':#' + offset.toString()], {}, (err, stdout, stderr) => {
try {
if (err && (<any>err).code === 'ENOENT') {
promptForMissingTool('gogetdoc');
}
if (err) return resolve(null);
let goGetDocOutput = <GoGetDocOuput>JSON.parse(stdout.toString());
let match = /(.*):(\d+):(\d+)/.exec(goGetDocOutput.pos);
let definitionInfo = {
file: null,
line: 0,
column: 0,
toolUsed: 'gogetdoc',
declarationlines: goGetDocOutput.decl.split('\n'),
doc: goGetDocOutput.doc
};
if (!match) {
return resolve(definitionInfo);
}
let [_, file, line, col] = match;
definitionInfo.file = match[1];
definitionInfo.line = +match[2] - 1;
definitionInfo.column = +match[3] - 1;
return resolve(definitionInfo);

} catch (e) {
reject(e);
}
});
let documentText = document.getText();
let documentArchive = document.fileName + '\n';
documentArchive = documentArchive + Buffer.byteLength(documentText) + '\n';
Expand All @@ -69,7 +156,7 @@ export class GoDefinitionProvider implements vscode.DefinitionProvider {
}
}

interface GoDocInfomation {
interface GoGetDocOuput {
name: string;
import: string;
decl: string;
Expand Down
12 changes: 4 additions & 8 deletions src/goExtraInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,15 @@ export class GoHoverProvider implements HoverProvider {
public provideHover(document: TextDocument, position: Position, token: CancellationToken): Thenable<Hover> {
return definitionLocation(document, position, true).then(definitionInfo => {
if (definitionInfo == null) return null;
let lines = definitionInfo.docInfo.decl.split('\n')
let lines = definitionInfo.declarationlines
.filter(line => !line.startsWith('\t//') && line !== '')
.map(line => line.replace(/\t/g, ' '));
let text;
if (lines.length > 1) {
text = lines.join('\n').replace(/\n+$/, '');
} else {
text = lines[0];
}
text = lines.join('\n').replace(/\n+$/, '');
let hoverTexts: MarkedString[] = [];
hoverTexts.push({ language: 'go', value: text });
if (definitionInfo.docInfo.doc != null) {
hoverTexts.push(definitionInfo.docInfo.doc);
if (definitionInfo.doc != null) {
hoverTexts.push(definitionInfo.doc);
}
let hover = new Hover(hoverTexts);
return hover;
Expand Down
23 changes: 19 additions & 4 deletions src/goSignature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,25 @@ export class GoSignatureHelpProvider implements SignatureHelpProvider {
return null;
}
let result = new SignatureHelp();
let text = res.docInfo.decl.substring(5);
let si = new SignatureInformation(text, res.docInfo.doc);
let braceStart = text.indexOf('(');
si.parameters = parameters(text.substring(braceStart)).map(paramText =>
let text, sig: string;
let si: SignatureInformation;
if (res.toolUsed === 'godef') {
// declaration is of the form "Add func(a int, b int) int"
text = res.declarationlines[0];
let nameEnd = text.indexOf(' ');
let sigStart = nameEnd + 5; // ' func'
let funcName = text.substring(0, nameEnd);
sig = text.substring(sigStart);
si = new SignatureInformation(funcName + sig, res.doc);
} else {
// declaration is of the form "func Add(a int, b int) int"
text = res.declarationlines[0].substring(5);
si = new SignatureInformation(text, res.doc);
let braceStart = text.indexOf('(');
sig = text.substring(braceStart);
}

si.parameters = parameters(sig).map(paramText =>
new ParameterInformation(paramText)
);
result.signatures = [si];
Expand Down
50 changes: 30 additions & 20 deletions test/go.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,28 +53,38 @@ encountered.
`;
let testCases: [vscode.Position, string, string][] = [
// [new vscode.Position(3,3), '/usr/local/go/src/fmt'],
[new vscode.Position(9, 6), 'func main()', null],
[new vscode.Position(7, 2), 'package fmt', null],
[new vscode.Position(7, 6), 'func Println(a ...interface{}) (n int, err error)', printlnDoc],
[new vscode.Position(10, 3), 'func print(txt string)', null]
[new vscode.Position(9, 6), 'main func()', null],
[new vscode.Position(7, 2), 'import (fmt "fmt")', null],
[new vscode.Position(7, 6), 'Println func(a ...interface{}) (n int, err error)', printlnDoc],
[new vscode.Position(10, 3), 'print func(txt string)', null]
];
let uri = vscode.Uri.file(path.join(fixturePath, 'test.go'));
vscode.workspace.openTextDocument(uri).then((textDocument) => {
let promises = testCases.map(([position, expectedSignature, expectedDocumentation]) =>
provider.provideHover(textDocument, position, null).then(res => {
// TODO: Documentation appears to currently be broken on Go 1.7, so disabling these tests for now
// if (expectedDocumentation === null) {
// assert.equal(res.contents.length, 1);
// } else {
// assert.equal(res.contents.length, 2);
// assert.equal(expectedDocumentation, <string>(res.contents[0]));
// }
assert.equal(expectedSignature, (<{ language: string; value: string }>res.contents[0]).value);
})
);
return Promise.all(promises);
}, (err) => {
assert.ok(false, `error in OpenTextDocument ${err}`);

getGoVersion().then(version => {
if (version.major > 1 || (version.major === 1 && version.minor > 5)) {
testCases[0][1] = 'func main()';
testCases[1][1] = 'package fmt';
testCases[2][1] = 'func Println(a ...interface{}) (n int, err error)';
testCases[3][1] = 'func print(txt string)';
}
return vscode.workspace.openTextDocument(uri).then((textDocument) => {
let promises = testCases.map(([position, expectedSignature, expectedDocumentation]) =>
provider.provideHover(textDocument, position, null).then(res => {
// TODO: Documentation appears to currently be broken on Go 1.7, so disabling these tests for now
// if (expectedDocumentation === null) {
// assert.equal(res.contents.length, 1);
// } else {
// assert.equal(res.contents.length, 2);
// assert.equal(expectedDocumentation, <string>(res.contents[0]));
// }
assert.equal(expectedSignature, (<{ language: string; value: string }>res.contents[0]).value);
})
);
return Promise.all(promises);
}, (err) => {
assert.ok(false, `error in OpenTextDocument ${err}`);
return Promise.reject(err);
});
}).then(() => done(), done);
});

Expand Down

0 comments on commit 0020119

Please sign in to comment.