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/setup-node@v1
|
||||
with:
|
||||
node-version: 12
|
||||
node-version: 14
|
||||
- run: npm install
|
||||
- name: Publish to Open VSX Registry
|
||||
uses: HaaLeo/publish-vscode-extension@v0
|
||||
with:
|
||||
|
@ -20,7 +21,8 @@ jobs:
|
|||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 12
|
||||
node-version: 14
|
||||
- run: npm install
|
||||
- name: Publish to Visual Studio Marketplace
|
||||
uses: HaaLeo/publish-vscode-extension@v0
|
||||
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:
|
||||
|
||||
<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
|
||||
|
|
19
package-lock.json
generated
19
package-lock.json
generated
|
@ -1,25 +1,30 @@
|
|||
{
|
||||
"name": "gotools",
|
||||
"version": "0.0.1",
|
||||
"version": "0.1.4",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "gotools",
|
||||
"version": "0.0.1",
|
||||
"version": "0.1.4",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"lodash.debounce": "^4.0.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/glob": "^8.1.0",
|
||||
"@types/mocha": "^10.0.1",
|
||||
"@types/node": "20.2.5",
|
||||
"@types/vscode": "^1.81.0",
|
||||
"@types/vscode": "^1.80.0",
|
||||
"@vscode/test-electron": "^2.3.2",
|
||||
"eslint": "^8.41.0",
|
||||
"glob": "^8.1.0",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"mocha": "^10.2.0",
|
||||
"typescript": "^5.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"vscode": "^1.81.0"
|
||||
"vscode": "^1.80.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@aashutoshrathi/word-wrap": {
|
||||
|
@ -1176,6 +1181,12 @@
|
|||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/lodash.debounce": {
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
|
||||
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/lodash.merge": {
|
||||
"version": "4.6.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
||||
|
|
20
package.json
20
package.json
|
@ -2,9 +2,9 @@
|
|||
"name": "gotools",
|
||||
"displayName": "Golang Tools",
|
||||
"description": "Tools for productive work",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.4",
|
||||
"engines": {
|
||||
"vscode": "^1.81.0"
|
||||
"vscode": "^1.80.0"
|
||||
},
|
||||
"publisher": "neonxp",
|
||||
"license": "GPL-3.0-or-later",
|
||||
|
@ -28,7 +28,8 @@
|
|||
"golang"
|
||||
],
|
||||
"activationEvents": [
|
||||
"onLanguage:go"
|
||||
"onLanguage:go",
|
||||
"onCommand:gotools.implement"
|
||||
],
|
||||
"main": "./src/extension.js",
|
||||
"contributes": {
|
||||
|
@ -50,6 +51,11 @@
|
|||
{
|
||||
"command": "gotools.wrap-error",
|
||||
"title": "if err≠nil {...}"
|
||||
},
|
||||
{
|
||||
"command": "gotools.implement",
|
||||
"title": "Implement Interface Methods",
|
||||
"category": "Go"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -59,14 +65,18 @@
|
|||
"test": "node ./test/runTest.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/vscode": "^1.81.0",
|
||||
"@types/glob": "^8.1.0",
|
||||
"@types/mocha": "^10.0.1",
|
||||
"@types/node": "20.2.5",
|
||||
"@types/vscode": "^1.80.0",
|
||||
"@vscode/test-electron": "^2.3.2",
|
||||
"eslint": "^8.41.0",
|
||||
"glob": "^8.1.0",
|
||||
"mocha": "^10.2.0",
|
||||
"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,11 +1,6 @@
|
|||
// 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 fnRegex = /^(\t*)(.*)err\s?:?=.+?$/;
|
||||
|
||||
// This method is called when your extension is activated
|
||||
// Your extension is activated the very first time the command is executed
|
||||
|
||||
const { ErrorsWrapper, wrapError } = require('./errors');
|
||||
const selectReceiver = require('./implement-interface');
|
||||
/**
|
||||
* @param {vscode.ExtensionContext} context
|
||||
*/
|
||||
|
@ -23,66 +18,20 @@ function activate(context) {
|
|||
)
|
||||
);
|
||||
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
|
||||
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 = {
|
||||
activate,
|
||||
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