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

Add command and code action to extract into function and local variable #2139

Merged
merged 13 commits into from
Mar 4, 2019
103 changes: 103 additions & 0 deletions src/goDoctor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*---------------------------------------------------------
* 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 { getBinPath, getToolsEnvVars } from './util';
import { promptForMissingTool } from './goInstallTools';
import { dirname, isAbsolute } from 'path';

/**
* Extracts function out of current selection and replaces the current selection with a call to the extracted function.
*/
export function extractFunction() {
extract('extract');
}

/**
* Extracts expression out of current selection into a var in the local scope and
* replaces the current selection with the new var.
*/
export function extractVariable() {
extract('var');
}

type typeOfExtraction = 'var' | 'extract';

async function extract(type: typeOfExtraction): Promise<void> {
let activeEditor = vscode.window.activeTextEditor;
if (!activeEditor) {
vscode.window.showInformationMessage('No editor is active.');
return;
}
if (activeEditor.selections.length !== 1) {
vscode.window.showInformationMessage(
`You need to have a single selection for extracting ${type === 'var' ? 'variable' : 'method'}`
);
return;
}

const newName = await vscode.window.showInputBox({
placeHolder: 'Please enter a name for the extracted variable.'
});

if (!newName) {
return;
}

runGoDoctor(
newName,
activeEditor.selection,
activeEditor.document.fileName,
type
);
}

/**
* @param newName name for the extracted method
* @param selection the editor selection from which method is to be extracted
* @param activeEditor the editor that will be used to apply the changes from godoctor
* @returns errorMessage in case the method fails, null otherwise
*/
function runGoDoctor(
newName: string,
selection: vscode.Selection,
fileName: string,
type: typeOfExtraction
): Thenable<void> {
const godoctor = getBinPath('godoctor');

return new Promise((resolve, reject) => {
if (!isAbsolute(godoctor)) {
promptForMissingTool('godoctor');
return resolve();
}

cp.execFile(
godoctor,
[
'-w',
'-pos',
`${selection.start.line + 1},${selection.start.character +
1}:${selection.end.line + 1},${selection.end.character}`,
'-file',
fileName,
type,
newName
],
{
env: getToolsEnvVars(),
cwd: dirname(fileName)
},
(err, stdout, stderr) => {
if (err) {
vscode.window.showErrorMessage(stderr || err.message);
}
}
);
});
}
7 changes: 5 additions & 2 deletions src/goInstallTools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ const _allTools: { [key: string]: string } = {
'go-langserver': 'github.com/sourcegraph/go-langserver',
'dlv': 'github.com/go-delve/delve/cmd/dlv',
'fillstruct': 'github.com/davidrjenni/reftools/cmd/fillstruct',
'godoctor': 'github.com/godoctor/godoctor',
};

function getToolImportPath(tool: string, goVersion: SemVersion) {
Expand Down Expand Up @@ -151,7 +152,8 @@ function getTools(goVersion: SemVersion): string[] {
'gomodifytags',
'impl',
'fillstruct',
'goplay'
'goplay',
'godoctor'
);

return tools;
Expand Down Expand Up @@ -183,7 +185,8 @@ export function installAllTools(updateExistingToolsOnly: boolean = false) {
'staticcheck': '\t(Linter)',
'go-langserver': '(Language Server)',
'dlv': '\t\t\t(Debugging)',
'fillstruct': '\t\t(Fill structs with defaults)'
'fillstruct': '\t\t(Fill structs with defaults)',
'godoctor': '\t\t(Extract to functions and variables)'
};

getGoVersion().then((goVersion) => {
Expand Down
9 changes: 9 additions & 0 deletions src/goMain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import { runFillStruct } from './goFillStruct';
import { parseLiveFile } from './goLiveErrors';
import { GoReferencesCodeLensProvider } from './goReferencesCodelens';
import { implCursor } from './goImpl';
import { extractFunction, extractVariable } from './goDoctor';
import { browsePackages } from './goBrowsePackage';
import { goGetPackage } from './goGetPackage';
import { GoDebugConfigurationProvider } from './goDebugConfiguration';
Expand All @@ -55,6 +56,7 @@ import { installCurrentPackage } from './goInstall';
import { setGlobalState } from './stateUtils';
import { ProvideTypeDefinitionSignature } from 'vscode-languageclient/lib/typeDefinition';
import { ProvideImplementationSignature } from 'vscode-languageclient/lib/implementation';
import { GoRefactorProvider } from './goRefactor';

export let buildDiagnosticCollection: vscode.DiagnosticCollection;
export let lintDiagnosticCollection: vscode.DiagnosticCollection;
Expand Down Expand Up @@ -271,6 +273,7 @@ export function activate(ctx: vscode.ExtensionContext): void {


ctx.subscriptions.push(vscode.languages.registerCodeActionsProvider(GO_MODE, new GoCodeActionProvider()));
ctx.subscriptions.push(vscode.languages.registerCodeActionsProvider(GO_MODE, new GoRefactorProvider()));
ctx.subscriptions.push(vscode.languages.registerCodeLensProvider(GO_MODE, testCodeLensProvider));
ctx.subscriptions.push(vscode.languages.registerCodeLensProvider(GO_MODE, referencesCodeLensProvider));
ctx.subscriptions.push(vscode.debug.registerDebugConfigurationProvider('go', new GoDebugConfigurationProvider()));
Expand Down Expand Up @@ -324,6 +327,12 @@ export function activate(ctx: vscode.ExtensionContext): void {
ctx.subscriptions.push(vscode.commands.registerCommand('go.impl.cursor', () => {
implCursor();
}));
ctx.subscriptions.push(vscode.commands.registerCommand('go.godoctor.extract', () => {
extractFunction();
}));
ctx.subscriptions.push(vscode.commands.registerCommand('go.godoctor.var', () => {
extractVariable();
}));

ctx.subscriptions.push(vscode.commands.registerCommand('go.test.cursor', (args) => {
const goConfig = vscode.workspace.getConfiguration('go', vscode.window.activeTextEditor ? vscode.window.activeTextEditor.document.uri : null);
Expand Down
36 changes: 36 additions & 0 deletions src/goRefactor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*---------------------------------------------------------
* 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');

export class GoRefactorProvider implements vscode.CodeActionProvider {
public provideCodeActions(
document: vscode.TextDocument,
range: vscode.Range,
context: vscode.CodeActionContext,
token: vscode.CancellationToken
): vscode.ProviderResult<vscode.CodeAction[]> {
const extractFunction = new vscode.CodeAction(
'Extract to function in package scope',
vscode.CodeActionKind.RefactorExtract
);
const extractVar = new vscode.CodeAction(
'Extract to variable in local scope',
vscode.CodeActionKind.RefactorExtract
);
extractFunction.command = {
title: 'Extract to function in package scope',
command: 'go.godoctor.extract'
};
extractVar.command = {
title: 'Extract to variable in local scope',
command: 'go.godoctor.var'
};
ramya-rao-a marked this conversation as resolved.
Show resolved Hide resolved

return [extractFunction, extractVar];
}
}