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

add fillstruct tool #1506

Merged
merged 7 commits into from
Feb 19, 2018
Merged
Show file tree
Hide file tree
Changes from 2 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
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ install:
- go get -u -v github.com/acroca/go-symbols
- go get -u -v github.com/cweill/gotests/...
- go get -u -v github.com/haya14busa/goplay/cmd/goplay
- go get -u -v github.com/davidrjenni/reftools/cmd/fillstruct
- GO15VENDOREXPERIMENT=1
- if [[ "$(go version)" =~ "go version go1.5" ]]; then echo skipping gometalinter; else go get -u -v github.com/alecthomas/gometalinter; fi
- if [[ "$(go version)" =~ "go version go1.5" ]]; then echo skipping gometalinter; else gometalinter --install; fi
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ This extension adds rich language support for the Go language to VS Code, includ
- Run Tests under the cursor, in current file, in current package, in the whole workspace (using `go test`)
- Show code coverage
- Generate method stubs for interfaces (using `impl`)
- Fill struct literals with default values (using `fillstruct`)
- [_partially implemented_] Debugging (using `delve`)
- Upload to the Go Playground (using `goplay`)

Expand Down Expand Up @@ -106,6 +107,7 @@ In addition to integrated editing features, the extension also provides several
* `Go: Add Tags` Adds configured tags to selected struct fields.
* `Go: Remove Tags` Removes configured tags from selected struct fields.
* `Go: Generate Interface Stubs` Generates method stubs for given interface
* `Go: Fill Struct` Fills struct literal with default values
* `Go: Run on Go Playground` Upload the current selection or file to the Go Playground

You can access all of the above commands from the command pallet (`Cmd+Shift+P` or `Ctrl+Shift+P`).
Expand Down
15 changes: 15 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,11 @@
"title": "Go: Remove Tags From Struct Fields",
"description": "Remove tags configured in go.removeTags setting from selected struct using gomodifytags"
},
{
"command": "go.fill.struct",
"title": "Go: Fill struct",
"description": "Fill a struct literal with default values"
},
{
"command": "go.show.commands",
"title": "Go: Show All Commands...",
Expand Down Expand Up @@ -829,6 +834,11 @@
"default": true,
"description": "If true, adds command to remove configured tags from struct fields to the editor context menu"
},
"fillStruct": {
"type": "boolean",
"default": true,
"description": "If true, adds command to fill struct literal with default values to the editor context menu"
},
"testAtCursor": {
"type": "boolean",
"default": true,
Expand Down Expand Up @@ -920,6 +930,11 @@
"command": "go.remove.tags",
"group": "Go group 1"
},
{
"when": "editorTextFocus && config.go.editorContextMenuCommands.fillStruct && resourceLangId == go",
"command": "go.fill.struct",
"group": "Go group 1"
},
{
"when": "editorTextFocus && config.go.editorContextMenuCommands.testAtCursor && resourceLangId == go && !config.editor.codeLens",
"command": "go.test.cursor",
Expand Down
100 changes: 100 additions & 0 deletions src/goFillStruct.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*---------------------------------------------------------
* Copyright (C) Microsoft Corporation. All rights reserved.
*--------------------------------------------------------*/

'use strict';

import vscode = require('vscode');
import { byteOffsetAt, getBinPath, getFileArchive, getToolsEnvVars } from './util';
import cp = require('child_process');
import { promptForMissingTool } from './goInstallTools';
import { outputChannel } from './goStatus';
import { TextEdit } from 'vscode-languageclient/lib/main';

// Interface for the output from fillstruct
interface GoFillStructOutput {
start: number;
end: number;
code: string;
}

export function runFillStruct(editor: vscode.TextEditor) {
let args = getCommonArgs(editor);
if (!args) {
return;
}

return execFillStruct(editor, args);
}

function getCommonArgs(editor: vscode.TextEditor): string[] {
if (!editor) {
vscode.window.showInformationMessage('No editor is active.');
return;
}
if (!editor.document.fileName.endsWith('.go')) {
vscode.window.showInformationMessage('Current file is not a Go file.');
return;
}
let args = ['-modified', '-file', editor.document.fileName];
if (editor.selection.isEmpty) {
let offset = byteOffsetAt(editor.document, editor.selection.start);
args.push('-offset');
args.push(offset.toString());
} else if (editor.selection.start.line <= editor.selection.end.line) {
args.push('-line');
args.push(`${editor.selection.start.line + 1},${editor.selection.end.line + 1}`);
Copy link
Contributor

Choose a reason for hiding this comment

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

When the command is run on a selection, fillstruct fails with invalid value "..." for flag -line: strconv.ParseInt: parsing "...": invalid syntax
Looks like the -line flag takes a single number and we are passing 2 here.

Also, in what scenario do we expect the user select text and then run the command?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I had that change sitting on my computer :) Since it takes a -line, I thought we might as well support selecting a whole line.

}

return args;
}

function getTabsCount(editor: vscode.TextEditor): number {
let startline = editor.selection.start.line;
let tabs = editor.document.lineAt(startline).text.match('^\t*');
return tabs.length;
}

function execFillStruct(editor: vscode.TextEditor, args: string[]): Promise<void> {
let fillstruct = getBinPath('fillstruct');
let input = getFileArchive(editor.document);
let tabsCount = getTabsCount(editor);

return new Promise<void>((resolve, reject) => {
let p = cp.execFile(fillstruct, args, { env: getToolsEnvVars() }, (err, stdout, stderr) => {
try {
if (err && (<any>err).code === 'ENOENT') {
promptForMissingTool('fillstruct');
return reject();
}
if (err) {
vscode.window.showInformationMessage(`Cannot fill struct: ${stderr}`);
return reject();
}

let output = <GoFillStructOutput[]>JSON.parse(stdout);

if (output.length === 0) {
vscode.window.showInformationMessage(`Got empty fillstruct output`);
return reject();
}

let indent = '\t'.repeat(tabsCount);
let edits: vscode.TextEdit[] = [];

return editor.edit(editBuilder => {
output.forEach((structToFill) => {
const out = structToFill.code.replace(/\n/g, '\n' + indent);
const rangeToReplace = new vscode.Range(editor.document.positionAt(structToFill.start),
editor.document.positionAt(structToFill.end));
editBuilder.replace(rangeToReplace, out);
});
resolve();
});
} catch (e) {
reject(e);
}
});
p.stdin.end(input);
});
}
6 changes: 4 additions & 2 deletions src/goInstallTools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ const allTools: { [key: string]: string } = {
'gometalinter': 'github.com/alecthomas/gometalinter',
'megacheck': 'honnef.co/go/tools/...',
'go-langserver': 'github.com/sourcegraph/go-langserver',
'dlv': 'github.com/derekparker/delve/cmd/dlv'
'dlv': 'github.com/derekparker/delve/cmd/dlv',
'fillstruct': 'github.com/davidrjenni/reftools/cmd/fillstruct'
};

// Tools used explicitly by the basic features of the extension
Expand Down Expand Up @@ -71,7 +72,8 @@ function getTools(goVersion: SemVersion): string[] {
'gorename',
'gomodifytags',
'goplay',
'impl'
'impl',
'fillstruct'
];

if (goLiveErrorsEnabled()) {
Expand Down
7 changes: 7 additions & 0 deletions src/goMain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import { isGoPathSet, getBinPath, sendTelemetryEvent, getExtensionCommands, getG
import { LanguageClient } from 'vscode-languageclient';
import { clearCacheForTools } from './goPath';
import { addTags, removeTags } from './goModifytags';
import { runFillStruct } from './goFillStruct';
import { parseLiveFile } from './goLiveErrors';
import { GoReferencesCodeLensProvider } from './goReferencesCodelens';
import { implCursor } from './goImpl';
Expand Down Expand Up @@ -188,6 +189,10 @@ export function activate(ctx: vscode.ExtensionContext): void {
removeTags(args);
}));

ctx.subscriptions.push(vscode.commands.registerCommand('go.fill.struct', () => {
runFillStruct(vscode.window.activeTextEditor);
}));

ctx.subscriptions.push(vscode.commands.registerCommand('go.impl.cursor', () => {
implCursor();
}));
Expand Down Expand Up @@ -397,6 +402,7 @@ function sendTelemetryEventForConfig(goConfig: vscode.WorkspaceConfiguration) {
"includeImports": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"addTags": { "classification": "CustomerContent", "purpose": "FeatureInsight" },
"removeTags": { "classification": "CustomerContent", "purpose": "FeatureInsight" },
"fillStruct": { "classification": "CustomerContent", "purpose": "FeatureInsight" },
"editorContextMenuCommands": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"liveErrors": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"codeLens": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
Expand Down Expand Up @@ -434,6 +440,7 @@ function sendTelemetryEventForConfig(goConfig: vscode.WorkspaceConfiguration) {
includeImports: goConfig['gotoSymbol'] && goConfig['gotoSymbol']['includeImports'] + '',
addTags: JSON.stringify(goConfig['addTags']),
removeTags: JSON.stringify(goConfig['removeTags']),
fillStruct: JSON.stringify(goConfig['fillStruct']),
Copy link
Contributor

Choose a reason for hiding this comment

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

This is only for configuration. Since there is no config called fillStruct, this change is not needed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

editorContextMenuCommands: JSON.stringify(goConfig['editorContextMenuCommands']),
liveErrors: JSON.stringify(goConfig['liveErrors']),
codeLens: JSON.stringify(goConfig['enableCodeLens'])
Expand Down
19 changes: 19 additions & 0 deletions test/fixtures/fillStruct/golden.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package main

import "time"

type Struct struct {
String string
Number int
Float float64
Time time.Time
}

func main() {
myStruct := Struct{
String: "",
Number: 0,
Float: 0.0,
Time: time.Time{},
}
}
14 changes: 14 additions & 0 deletions test/fixtures/fillStruct/input.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package main

import "time"

type Struct struct {
String string
Number int
Float float64
Time time.Time
}

func main() {
myStruct := Struct{}
}
5 changes: 4 additions & 1 deletion test/go.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import { getAllPackages } from '../src/goPackages';
import { getImportPath } from '../src/util';
import { goPlay } from '../src/goPlayground';
import { goLint } from '../src/goLint';
import { runFillStruct } from '../src/goFillStruct';
import { print } from 'util';

suite('Go Extension Tests', () => {
let gopath = process.env['GOPATH'];
Expand Down Expand Up @@ -61,6 +63,8 @@ suite('Go Extension Tests', () => {
fs.copySync(path.join(fixtureSourcePath, 'importTest', 'noimports.go'), path.join(fixturePath, 'importTest', 'noimports.go'));
fs.copySync(path.join(fixtureSourcePath, 'importTest', 'groupImports.go'), path.join(fixturePath, 'importTest', 'groupImports.go'));
fs.copySync(path.join(fixtureSourcePath, 'importTest', 'singleImports.go'), path.join(fixturePath, 'importTest', 'singleImports.go'));
fs.copySync(path.join(fixtureSourcePath, 'fillStruct', 'input.go'), path.join(fixturePath, 'fillStruct', 'input.go'));
fs.copySync(path.join(fixtureSourcePath, 'fillStruct', 'golden.go'), path.join(fixturePath, 'fillStruct', 'golden.go'));
});

suiteTeardown(() => {
Expand Down Expand Up @@ -956,5 +960,4 @@ It returns the number of bytes written and any write error encountered.
});
}).then(() => done(), done);
});

});