Skip to content

Commit 3623adc

Browse files
committed
src/goGenerateMethod: make method generation more automatic using code action
1 parent d31a6c5 commit 3623adc

File tree

3 files changed

+131
-0
lines changed

3 files changed

+131
-0
lines changed

package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,11 @@
353353
"title": "Go: Generate Unit Tests For Function",
354354
"description": "Generates unit tests for the selected function in the current file"
355355
},
356+
{
357+
"command": "go.generate.method",
358+
"title": "Go: Generate Method",
359+
"description": "Generates method for the given type"
360+
},
356361
{
357362
"command": "go.impl.cursor",
358363
"title": "Go: Generate Interface Stubs",

src/goGenerateMethod.ts

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import vscode = require('vscode');
2+
import { CommandFactory } from './commands';
3+
4+
const CommandTitle = 'Generate method';
5+
const Command = 'go.generate.method';
6+
7+
export const goGenerateMethod: CommandFactory = () => (
8+
uncommontypeName: string,
9+
needPtrReceiver: boolean,
10+
endPos: vscode.Position
11+
) => {
12+
const editor = vscode.window.activeTextEditor;
13+
if (!editor) {
14+
vscode.window.showErrorMessage('No active editor found.');
15+
return;
16+
}
17+
const receiverName = getReceiverName(uncommontypeName);
18+
let methodTpl = `\n\nfunc ($\{1:${receiverName}} *${uncommontypeName}) $\{2:methodName}($3) $4 {\n\t$5\n}$0`;
19+
if (!needPtrReceiver) {
20+
methodTpl = `\n\nfunc ($\{1:${receiverName}} ${uncommontypeName}) $\{2:methodName}($3) $4 {\n\t$5\n}$0`;
21+
}
22+
editor.insertSnippet(new vscode.SnippetString(methodTpl), endPos);
23+
};
24+
25+
export class MethodGenerationProvider implements vscode.CodeActionProvider {
26+
public static readonly providedCodeActionKinds = [vscode.CodeActionKind.Refactor];
27+
28+
async provideCodeActions(
29+
document: vscode.TextDocument,
30+
range: vscode.Range | vscode.Selection,
31+
_context: vscode.CodeActionContext,
32+
_token: vscode.CancellationToken
33+
): Promise<vscode.CodeAction[] | undefined> {
34+
const lineText = document.lineAt(range.start.line).text;
35+
// TODO(sslime336): support the uncommontypes defined in type block.
36+
const uncommontypeName = await this.getUncommontypeName(lineText);
37+
38+
if (uncommontypeName === '') {
39+
return;
40+
}
41+
42+
let documentSymbols: vscode.DocumentSymbol[] = [];
43+
await vscode.commands
44+
.executeCommand<vscode.DocumentSymbol[]>('vscode.executeDocumentSymbolProvider', document.uri)
45+
.then((symbols) => {
46+
documentSymbols = symbols.filter((symbol) => {
47+
const res = symbol.name === uncommontypeName;
48+
return res;
49+
});
50+
});
51+
52+
if (documentSymbols.length === 0) {
53+
return;
54+
}
55+
56+
const endPos = documentSymbols[0].range.end;
57+
58+
const genPointerReceiverMethod = new vscode.CodeAction(
59+
'generate method with pointer receiver',
60+
vscode.CodeActionKind.Refactor
61+
);
62+
genPointerReceiverMethod.command = {
63+
title: CommandTitle,
64+
command: Command,
65+
arguments: [uncommontypeName, true, endPos]
66+
};
67+
68+
const genValueReceiverMethod = new vscode.CodeAction(
69+
'generate method with value receiver',
70+
vscode.CodeActionKind.Refactor
71+
);
72+
genValueReceiverMethod.command = {
73+
title: CommandTitle,
74+
command: Command,
75+
arguments: [uncommontypeName, false, endPos]
76+
};
77+
78+
return [genPointerReceiverMethod, genValueReceiverMethod];
79+
}
80+
81+
resolveCodeAction?(
82+
_codeAction: vscode.CodeAction,
83+
_token: vscode.CancellationToken
84+
): vscode.ProviderResult<vscode.CodeAction> {
85+
return;
86+
}
87+
88+
// getUncommontypeName returns the user defined type's name from type definitions
89+
// that starts with single keyword type.
90+
// The types defined in the type definition block will not satisfied the regexp.
91+
private async getUncommontypeName(lineText: string): Promise<string> {
92+
const regexp = /type ([^0-9]\w+) [^0-9]\w+/;
93+
const matches = lineText.match(regexp);
94+
if (!matches) {
95+
return '';
96+
}
97+
return matches[1];
98+
}
99+
}
100+
101+
// getReceiverName returns the default receiver name which is constructed with uppercase
102+
// characters picked from the structName, it will return the first character in lowercase if
103+
// the structName contains no uppercased character.
104+
function getReceiverName(structName: string): string {
105+
let res = '';
106+
structName
107+
.split('')
108+
.filter((ch) => isUpperCase(ch))
109+
.forEach((ch) => (res += ch.toLowerCase()));
110+
if (res === '') {
111+
res = structName.charAt(0);
112+
}
113+
return res;
114+
}
115+
116+
const isUpperCase = (ch: string): boolean => {
117+
const c = ch.charCodeAt(0);
118+
return 65 <= c && c <= 90;
119+
};

src/goMain.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ import { GoExplorerProvider } from './goExplorer';
6666
import { GoExtensionContext } from './context';
6767
import * as commands from './commands';
6868
import { toggleVulncheckCommandFactory, VulncheckOutputLinkProvider } from './goVulncheck';
69+
import { MethodGenerationProvider, goGenerateMethod } from './goGenerateMethod';
6970

7071
const goCtx: GoExtensionContext = {};
7172

@@ -132,6 +133,12 @@ export async function activate(ctx: vscode.ExtensionContext): Promise<ExtensionA
132133
goCtx.vetDiagnosticCollection = vscode.languages.createDiagnosticCollection('go-vet');
133134
ctx.subscriptions.push(goCtx.vetDiagnosticCollection);
134135

136+
const genStructMethodProvidor = vscode.languages.registerCodeActionsProvider('go', new MethodGenerationProvider(), {
137+
providedCodeActionKinds: MethodGenerationProvider.providedCodeActionKinds
138+
});
139+
ctx.subscriptions.push(genStructMethodProvidor);
140+
141+
registerCommand('go.generate.method', goGenerateMethod);
135142
registerCommand('go.gopath', commands.getCurrentGoPath);
136143
registerCommand('go.goroot', commands.getCurrentGoRoot);
137144
registerCommand('go.locate.tools', commands.getConfiguredGoTools);

0 commit comments

Comments
 (0)