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

Use gogetdoc instead of godef and godoc #622

Merged
merged 11 commits into from
Nov 20, 2016
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);
Copy link
Contributor

Choose a reason for hiding this comment

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

Does that work for methods that have a signature like func (l *Struct) Add(a int, b int) {? I did it somewhat differently for that reason https://github.com/abarisain/vscode-go/blob/gogetdoc2/src/goSignature.ts#L33-L40

If it does, then sorry! Haven't had the time to test this branch yet.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In such case it thinks the receiver is the first param, thats a bug.

Copy link
Contributor Author

@ramya-rao-a ramya-rao-a Nov 18, 2016

Choose a reason for hiding this comment

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

Fixed it, Took your code and tested. Will push the changes soon.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@abarisain The changes are in

}

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
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we give reenabling these tests a shot? When this was commented, they worked on my machine

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@abarisain Done!

// 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