interface implementation
This commit is contained in:
parent
0eaa18df4b
commit
80815c107e
8 changed files with 2191 additions and 2030 deletions
6
.github/workflows/publish.yml
vendored
6
.github/workflows/publish.yml
vendored
|
@ -9,7 +9,8 @@ jobs:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- uses: actions/setup-node@v1
|
- uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: 12
|
node-version: 14
|
||||||
|
- run: npm install
|
||||||
- name: Publish to Open VSX Registry
|
- name: Publish to Open VSX Registry
|
||||||
uses: HaaLeo/publish-vscode-extension@v0
|
uses: HaaLeo/publish-vscode-extension@v0
|
||||||
with:
|
with:
|
||||||
|
@ -20,7 +21,8 @@ jobs:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- uses: actions/setup-node@v1
|
- uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: 12
|
node-version: 14
|
||||||
|
- run: npm install
|
||||||
- name: Publish to Visual Studio Marketplace
|
- name: Publish to Visual Studio Marketplace
|
||||||
uses: HaaLeo/publish-vscode-extension@v0
|
uses: HaaLeo/publish-vscode-extension@v0
|
||||||
with:
|
with:
|
||||||
|
|
17
README.md
17
README.md
|
@ -20,3 +20,20 @@ Extension in active development! Your contribution is always welcome :)
|
||||||
`Add error checking` - adds stub error checking to current line:
|
`Add error checking` - adds stub error checking to current line:
|
||||||
|
|
||||||
<img src="/wraperror.gif" width="500" />
|
<img src="/wraperror.gif" width="500" />
|
||||||
|
|
||||||
|
|
||||||
|
# Interface implementation
|
||||||
|
|
||||||
|
Command "Go: Implement Interface Methods" based on https://github.com/ricardoerikson/vscode-go-impl-methods/ extension.
|
||||||
|
|
||||||
|
Install the impl package as follows:
|
||||||
|
|
||||||
|
```
|
||||||
|
go get -u github.com/josharian/impl
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
1. At command pallete select command "Go: Implement Interface Methods"
|
||||||
|
2. Write receiver for methods. Example: "f *File", "m MyType", "c CustomType"
|
||||||
|
3. Select interface to implement
|
||||||
|
|
3909
package-lock.json
generated
3909
package-lock.json
generated
File diff suppressed because it is too large
Load diff
20
package.json
20
package.json
|
@ -2,9 +2,9 @@
|
||||||
"name": "gotools",
|
"name": "gotools",
|
||||||
"displayName": "Golang Tools",
|
"displayName": "Golang Tools",
|
||||||
"description": "Tools for productive work",
|
"description": "Tools for productive work",
|
||||||
"version": "0.1.2",
|
"version": "0.1.4",
|
||||||
"engines": {
|
"engines": {
|
||||||
"vscode": "^1.81.0"
|
"vscode": "^1.80.0"
|
||||||
},
|
},
|
||||||
"publisher": "neonxp",
|
"publisher": "neonxp",
|
||||||
"license": "GPL-3.0-or-later",
|
"license": "GPL-3.0-or-later",
|
||||||
|
@ -28,7 +28,8 @@
|
||||||
"golang"
|
"golang"
|
||||||
],
|
],
|
||||||
"activationEvents": [
|
"activationEvents": [
|
||||||
"onLanguage:go"
|
"onLanguage:go",
|
||||||
|
"onCommand:gotools.implement"
|
||||||
],
|
],
|
||||||
"main": "./src/extension.js",
|
"main": "./src/extension.js",
|
||||||
"contributes": {
|
"contributes": {
|
||||||
|
@ -50,6 +51,11 @@
|
||||||
{
|
{
|
||||||
"command": "gotools.wrap-error",
|
"command": "gotools.wrap-error",
|
||||||
"title": "if err≠nil {...}"
|
"title": "if err≠nil {...}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "gotools.implement",
|
||||||
|
"title": "Implement Interface Methods",
|
||||||
|
"category": "Go"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -59,14 +65,18 @@
|
||||||
"test": "node ./test/runTest.js"
|
"test": "node ./test/runTest.js"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/vscode": "^1.81.0",
|
|
||||||
"@types/glob": "^8.1.0",
|
"@types/glob": "^8.1.0",
|
||||||
"@types/mocha": "^10.0.1",
|
"@types/mocha": "^10.0.1",
|
||||||
"@types/node": "20.2.5",
|
"@types/node": "20.2.5",
|
||||||
|
"@types/vscode": "^1.80.0",
|
||||||
|
"@vscode/test-electron": "^2.3.2",
|
||||||
"eslint": "^8.41.0",
|
"eslint": "^8.41.0",
|
||||||
"glob": "^8.1.0",
|
"glob": "^8.1.0",
|
||||||
"mocha": "^10.2.0",
|
"mocha": "^10.2.0",
|
||||||
"typescript": "^5.1.3",
|
"typescript": "^5.1.3",
|
||||||
"@vscode/test-electron": "^2.3.2"
|
"lodash.debounce": "^4.0.8"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"lodash.debounce": "^4.0.8"
|
||||||
}
|
}
|
||||||
}
|
}
|
60
src/errors.js
Normal file
60
src/errors.js
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
const vscode = require('vscode');
|
||||||
|
|
||||||
|
const fnRegex = /^(\t*)(.*)err\s?:?=.+?$/;
|
||||||
|
|
||||||
|
class ErrorsWrapper {
|
||||||
|
provideCodeActions(document, range) {
|
||||||
|
const editor = vscode.window.activeTextEditor;
|
||||||
|
if (!editor) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const line = document.lineAt(editor.selection.start.line);
|
||||||
|
if (!fnRegex.test(line.text)) {
|
||||||
|
vscode.commands.executeCommand('setContext', 'allowWrapIferr', false);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
vscode.commands.executeCommand('setContext', 'allowWrapIferr', true);
|
||||||
|
const action = new vscode.CodeAction('Add error checking', vscode.CodeActionKind.RefactorRewrite);
|
||||||
|
action.command = { command: 'gotools.wrap-error', title: 'Add error checking block', tooltip: '' };
|
||||||
|
return [
|
||||||
|
action,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const wrapError = () => {
|
||||||
|
const editor = vscode.window.activeTextEditor;
|
||||||
|
if (!editor) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const document = editor.document;
|
||||||
|
|
||||||
|
const line = document.lineAt(editor.selection.start.line);
|
||||||
|
const matches = line.text.match(fnRegex);
|
||||||
|
if (matches === null || matches.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const extravars = matches[2].split(',').map(x => x.trim()).filter(x => x);
|
||||||
|
if (extravars.filter(x => x !== "_").length > 0) {
|
||||||
|
editor.insertSnippet(
|
||||||
|
new vscode.SnippetString(`\nif err != nil {\n\treturn \${1:nil, }\${2:err}\n}\n`),
|
||||||
|
new vscode.Position(line.range.end.line, line.range.end.character + line.firstNonWhitespaceCharacterIndex),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const tabs = matches[1];
|
||||||
|
const original = matches[0].trimStart()
|
||||||
|
editor.insertSnippet(
|
||||||
|
new vscode.SnippetString(`${tabs}if ${original}; err != nil {\n${tabs}\treturn \${2:nil, }\${3:err}\n${tabs}}\n`),
|
||||||
|
line.range,
|
||||||
|
{
|
||||||
|
undoStopBefore: true,
|
||||||
|
undoStopAfter: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
ErrorsWrapper, wrapError
|
||||||
|
}
|
|
@ -1,88 +1,37 @@
|
||||||
// The module 'vscode' contains the VS Code extensibility API
|
|
||||||
// Import the module and reference it with the alias vscode in your code below
|
|
||||||
const vscode = require('vscode');
|
const vscode = require('vscode');
|
||||||
const fnRegex = /^(\t*)(.*)err\s?:?=.+?$/;
|
const { ErrorsWrapper, wrapError } = require('./errors');
|
||||||
|
const selectReceiver = require('./implement-interface');
|
||||||
// This method is called when your extension is activated
|
|
||||||
// Your extension is activated the very first time the command is executed
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {vscode.ExtensionContext} context
|
* @param {vscode.ExtensionContext} context
|
||||||
*/
|
*/
|
||||||
function activate(context) {
|
function activate(context) {
|
||||||
let wrapErrorCommand = vscode.commands.registerCommand('gotools.wrap-error', wrapError);
|
let wrapErrorCommand = vscode.commands.registerCommand('gotools.wrap-error', wrapError);
|
||||||
context.subscriptions.push(
|
context.subscriptions.push(
|
||||||
vscode.languages.registerCodeActionsProvider(
|
vscode.languages.registerCodeActionsProvider(
|
||||||
'go',
|
'go',
|
||||||
new ErrorsWrapper(),
|
new ErrorsWrapper(),
|
||||||
{
|
{
|
||||||
providedCodeActionKinds: [
|
providedCodeActionKinds: [
|
||||||
vscode.CodeActionKind.RefactorRewrite
|
vscode.CodeActionKind.RefactorRewrite
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
context.subscriptions.push(wrapErrorCommand);
|
context.subscriptions.push(wrapErrorCommand);
|
||||||
|
|
||||||
|
vscode.commands.registerCommand("gotools.imports", function () {
|
||||||
|
// TODO
|
||||||
|
})
|
||||||
|
|
||||||
|
context.subscriptions.push(vscode.commands.registerCommand("gotools.implement", selectReceiver));
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method is called when your extension is deactivated
|
// This method is called when your extension is deactivated
|
||||||
function deactivate() { }
|
function deactivate() { }
|
||||||
|
|
||||||
class ErrorsWrapper {
|
|
||||||
|
|
||||||
provideCodeActions(document, range) {
|
|
||||||
const editor = vscode.window.activeTextEditor;
|
|
||||||
if (!editor) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
const line = document.lineAt(editor.selection.start.line);
|
|
||||||
if (!fnRegex.test(line.text)) {
|
|
||||||
vscode.commands.executeCommand('setContext', 'allowWrapIferr', false);
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
vscode.commands.executeCommand('setContext', 'allowWrapIferr', true);
|
|
||||||
const action = new vscode.CodeAction('Add error checking', vscode.CodeActionKind.RefactorRewrite);
|
|
||||||
action.command = { command: 'gotools.wrap-error', title: 'Add error checking block', tooltip: '' };
|
|
||||||
return [
|
|
||||||
action,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
activate,
|
activate,
|
||||||
deactivate
|
deactivate
|
||||||
}
|
}
|
||||||
|
|
||||||
const wrapError = () => {
|
|
||||||
const editor = vscode.window.activeTextEditor;
|
|
||||||
if (!editor) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const document = editor.document;
|
|
||||||
|
|
||||||
const line = document.lineAt(editor.selection.start.line);
|
|
||||||
const matches = line.text.match(fnRegex);
|
|
||||||
if (matches === null || matches.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const extravars = matches[2].split(',').map(x => x.trim()).filter(x => x);
|
|
||||||
if (extravars.filter(x => x !== "_").length > 0) {
|
|
||||||
editor.insertSnippet(
|
|
||||||
new vscode.SnippetString(`\nif err != nil {\n\treturn \${1:nil, }\${2:err}\n}\n`),
|
|
||||||
new vscode.Position(line.range.end.line, line.range.end.character + line.firstNonWhitespaceCharacterIndex),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
const tabs = matches[1];
|
|
||||||
const original = matches[0].trimStart()
|
|
||||||
editor.insertSnippet(
|
|
||||||
new vscode.SnippetString(`${tabs}if ${original}; err != nil {\n${tabs}\treturn \${2:nil, }\${3:err}\n${tabs}}\n`),
|
|
||||||
line.range,
|
|
||||||
{
|
|
||||||
undoStopBefore: true,
|
|
||||||
undoStopAfter: true
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
95
src/implement-interface.js
Normal file
95
src/implement-interface.js
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
const vscode = require('vscode');
|
||||||
|
const { dirname } = require('path');
|
||||||
|
const cp = require('child_process');
|
||||||
|
const provideInterfaces = require('./interfaces-provider');
|
||||||
|
const debounce = require('lodash.debounce');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function selectReceiver() {
|
||||||
|
const receiverInput = vscode.window.createInputBox();
|
||||||
|
const pattern = /^([a-zA-Z_][a-zA-Z0-9_]*)\s+(\*?(?:[a-zA-Z_][a-zA-Z0-9]*\.)?[a-zA-Z_][a-zA-Z0-9_]*)$/;
|
||||||
|
receiverInput.placeholder = "Enter receiver (ex: 'm *MyType')";
|
||||||
|
|
||||||
|
receiverInput.onDidChangeValue(value => {
|
||||||
|
if (value != "" && !value.match(pattern)) {
|
||||||
|
receiverInput.validationMessage = `Valid format: "f *File", "m MyType", "c CustomType"`;
|
||||||
|
} else {
|
||||||
|
receiverInput.validationMessage = '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
receiverInput.onDidAccept(e => {
|
||||||
|
const receiver = receiverInput.value;
|
||||||
|
const matches = receiver.match(pattern);
|
||||||
|
if (!matches) {
|
||||||
|
vscode.window.showWarningMessage(`Receiver is not in the correct format. Valid: "f *File", "m MyType", "c CustomType"`);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
selectInterface({ name: matches[1], type_: matches[2] });
|
||||||
|
receiverInput.hide();
|
||||||
|
});
|
||||||
|
|
||||||
|
receiverInput.onDidHide(() => {
|
||||||
|
receiverInput.dispose();
|
||||||
|
});
|
||||||
|
receiverInput.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {*} receiver
|
||||||
|
*/
|
||||||
|
function selectInterface(receiver) {
|
||||||
|
const quickPick = vscode.window.createQuickPick();
|
||||||
|
quickPick.placeholder = "Which interface would you like to implement?";
|
||||||
|
const debounced = debounce((value) => {
|
||||||
|
provideInterfaces(value, (interfaces) => {
|
||||||
|
const items = interfaces.map((label) => ({ label }));
|
||||||
|
quickPick.items = items;
|
||||||
|
});
|
||||||
|
}, 400, { trailing: true });
|
||||||
|
|
||||||
|
quickPick.onDidChangeValue(value => {
|
||||||
|
debounced(value);
|
||||||
|
});
|
||||||
|
|
||||||
|
quickPick.onDidChangeSelection(selection => {
|
||||||
|
if (selection[0]) {
|
||||||
|
implement(selection[0].label, receiver);
|
||||||
|
}
|
||||||
|
quickPick.hide();
|
||||||
|
});
|
||||||
|
|
||||||
|
quickPick.onDidHide(() => {
|
||||||
|
quickPick.dispose();
|
||||||
|
});
|
||||||
|
quickPick.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
function implement(interface_, receiver) {
|
||||||
|
const editor = vscode.window.activeTextEditor;
|
||||||
|
vscode.window.withProgress({
|
||||||
|
location: vscode.ProgressLocation.Notification,
|
||||||
|
title: "Generating stub methods..."
|
||||||
|
}, (progress, token) => {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const r = `${receiver.name} ${receiver.type_}`
|
||||||
|
cp.exec(`impl "${r}" ${interface_}`,
|
||||||
|
{ cwd: dirname(editor.document.fileName) },
|
||||||
|
(error, stdout, stderr) => {
|
||||||
|
if (error) {
|
||||||
|
vscode.window.showInformationMessage(stderr);
|
||||||
|
return resolve(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
const position = editor.selection.active;
|
||||||
|
|
||||||
|
editor.insertSnippet(new vscode.SnippetString("\n" + stdout), position.with(position.line+1, 0));
|
||||||
|
resolve(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = selectReceiver;
|
17
src/interfaces-provider.js
Normal file
17
src/interfaces-provider.js
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
const vscode = require('vscode');
|
||||||
|
|
||||||
|
function provideInterfaces(keyword, callback) {
|
||||||
|
vscode.commands.executeCommand("vscode.executeWorkspaceSymbolProvider", keyword)
|
||||||
|
.then(
|
||||||
|
/**
|
||||||
|
* @param {array} objects
|
||||||
|
*/
|
||||||
|
(objects) => {
|
||||||
|
const interfaces = objects.
|
||||||
|
filter(x => x.kind == vscode.SymbolKind.Interface).
|
||||||
|
map(x => x.name)
|
||||||
|
callback(interfaces);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = provideInterfaces
|
Loading…
Reference in a new issue