From a8fa5bf1cf7a818f73910477c69040160a4d14a3 Mon Sep 17 00:00:00 2001 From: NeonXP Date: Tue, 8 Aug 2023 02:40:09 +0300 Subject: [PATCH] group imports command --- README.md | 11 ++++- package.json | 7 ++- src/extension.js | 5 +- src/group-imports.js | 97 ++++++++++++++++++++++++++++++++++++++ src/implement-interface.js | 18 +++---- src/interfaces-provider.js | 4 +- src/utils.js | 52 ++++++++++++++++++++ 7 files changed, 176 insertions(+), 18 deletions(-) create mode 100644 src/group-imports.js create mode 100644 src/utils.js diff --git a/README.md b/README.md index ece4d77..62f8f8a 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Extension in active development! Your contribution is always welcome :) # Interface implementation -Command "Go: Implement Interface Methods" based on https://github.com/ricardoerikson/vscode-go-impl-methods/ extension. +Command "Go: Implement Interface Methods" based on https://github.com/ricardoerikson/vscode-go-impl-methods/ extension by Ricardo Erikson. Install the impl package as follows: @@ -37,3 +37,12 @@ go get -u github.com/josharian/impl 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 + + +# Group imports + +Group imports command based on https://github.com/gustavo-bordin/golang-imports-group/ extension by Gustavo Bordin. + +## Usage + +1. At command pallete select command "Go: Group imports" \ No newline at end of file diff --git a/package.json b/package.json index ad85d18..1d0d65f 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "gotools", "displayName": "Golang Tools", "description": "Tools for productive work", - "version": "0.1.4", + "version": "0.1.5", "engines": { "vscode": "^1.80.0" }, @@ -56,6 +56,11 @@ "command": "gotools.implement", "title": "Implement Interface Methods", "category": "Go" + }, + { + "command": "gotools.group-imports", + "title": "Group imports", + "category": "Go" } ] }, diff --git a/src/extension.js b/src/extension.js index 850d5be..81ba639 100644 --- a/src/extension.js +++ b/src/extension.js @@ -1,6 +1,7 @@ const vscode = require('vscode'); const { ErrorsWrapper, wrapError } = require('./errors'); const selectReceiver = require('./implement-interface'); +const groupImports = require('./group-imports'); /** * @param {vscode.ExtensionContext} context */ @@ -19,9 +20,7 @@ function activate(context) { ); context.subscriptions.push(wrapErrorCommand); - vscode.commands.registerCommand("gotools.imports", function () { - // TODO - }) + context.subscriptions.push(vscode.commands.registerCommand("gotools.group-imports", groupImports)) context.subscriptions.push(vscode.commands.registerCommand("gotools.implement", selectReceiver)); } diff --git a/src/group-imports.js b/src/group-imports.js new file mode 100644 index 0000000..00300e4 --- /dev/null +++ b/src/group-imports.js @@ -0,0 +1,97 @@ +const vscode = require('vscode'); +const { getModuleName, getImportsRange, getFileContent } = require('./utils'); + +const BUILTIN_TYPE = "builtin" +const THIRD_PARTY_TYPE = "thirdParty" +const VENDOR_TYPE = "vendor" +const LOCAL_TYPE = "local" + + +function getImports(fileContent) { + try { + let importsRegex = new RegExp("(?<=import \\(\n).*?(?=\\))", "s") + let imports = importsRegex.exec(fileContent)[0] + return imports + } catch(_) { + vscode.window.showErrorMessage("Could not find imports") + } +} + +function convertImportsToList(imports) { + return imports.split("\n") +} + +function getImportType(import_) { + let goModName = getModuleName() + "/" + let vendorPath = goModName.substring(0, goModName.lastIndexOf('/')); + + let isBuiltin = !import_.includes(".") && !import_.includes(goModName) + let isThirdParty = import_.includes(".") && !import_.includes(goModName) + let isLocal = import_.includes(goModName) + let isVendor = import_.includes(vendorPath) && !isLocal + + if(isBuiltin) { + return BUILTIN_TYPE + } else if (isVendor) { + return VENDOR_TYPE + } else if(isThirdParty) { + return THIRD_PARTY_TYPE + } else if(isLocal) { + return LOCAL_TYPE + } +} + +function importGroupsToString(importsGroup) { + return Object.keys(importsGroup) + .filter((key) => importsGroup[key].length) + .map((key) => importsGroup[key].join('\n')) + .join('\n\n'); +} + +function saveImportsGroup(importsGroup, importRanges, activeEditor) { + const edit = new vscode.WorkspaceEdit(); + const range = new vscode.Range( + importRanges.start, + 0, + importRanges.end - 1, + Number.MAX_VALUE + ); + + let importsParsed = importGroupsToString(importsGroup) + let documentUri = activeEditor.document.uri + + edit.replace(documentUri, range, importsParsed); + vscode.workspace.applyEdit(edit).then(activeEditor.document.save); +} + + +function getImportGroups(importsList) { + const importsGroup = { + [BUILTIN_TYPE]: [], + [THIRD_PARTY_TYPE]: [], + [VENDOR_TYPE]: [], + [LOCAL_TYPE]: [], + }; + + + importsList.filter(n => n).forEach(import_ => { + let importType = getImportType(import_) + importsGroup[importType].push(import_) + }) + + return importsGroup +} + +function formatImports() { + const activeEditor = vscode.window.activeTextEditor + let fileContent = getFileContent(activeEditor) + let imports = getImports(fileContent) + let importsList = convertImportsToList(imports) + let importsGroup = getImportGroups(importsList) + let importsRange = getImportsRange(fileContent) + saveImportsGroup(importsGroup,importsRange,activeEditor) +} + + + +module.exports = formatImports; \ No newline at end of file diff --git a/src/implement-interface.js b/src/implement-interface.js index 8c6f3a3..a165902 100644 --- a/src/implement-interface.js +++ b/src/implement-interface.js @@ -1,11 +1,9 @@ const vscode = require('vscode'); const { dirname } = require('path'); const cp = require('child_process'); -const provideInterfaces = require('./interfaces-provider'); +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_]*)$/; @@ -15,11 +13,11 @@ function selectReceiver() { if (value != "" && !value.match(pattern)) { receiverInput.validationMessage = `Valid format: "f *File", "m MyType", "c CustomType"`; } else { - receiverInput.validationMessage = ''; + receiverInput.validationMessage = ''; } }); - receiverInput.onDidAccept(e => { + receiverInput.onDidAccept(() => { const receiver = receiverInput.value; const matches = receiver.match(pattern); if (!matches) { @@ -45,8 +43,7 @@ function selectInterface(receiver) { 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; + quickPick.items = interfaces; }); }, 400, { trailing: true }); @@ -56,7 +53,7 @@ function selectInterface(receiver) { quickPick.onDidChangeSelection(selection => { if (selection[0]) { - implement(selection[0].label, receiver); + implement(selection[0].detail + '.' + selection[0].label, receiver); } quickPick.hide(); }); @@ -72,7 +69,7 @@ function implement(interface_, receiver) { 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_}`, @@ -84,8 +81,7 @@ function implement(interface_, receiver) { } const position = editor.selection.active; - - editor.insertSnippet(new vscode.SnippetString("\n" + stdout), position.with(position.line+1, 0)); + editor.insertSnippet(new vscode.SnippetString("\n" + stdout), position.with(position.line + 1, 0)); resolve(true); }); }); diff --git a/src/interfaces-provider.js b/src/interfaces-provider.js index d42995e..969c562 100644 --- a/src/interfaces-provider.js +++ b/src/interfaces-provider.js @@ -9,9 +9,9 @@ function provideInterfaces(keyword, callback) { (objects) => { const interfaces = objects. filter(x => x.kind == vscode.SymbolKind.Interface). - map(x => x.name) + map(x => ({ label: x.name, detail: x.containerName })) callback(interfaces); }); } -module.exports = provideInterfaces \ No newline at end of file +module.exports = { provideInterfaces }; \ No newline at end of file diff --git a/src/utils.js b/src/utils.js new file mode 100644 index 0000000..eddee94 --- /dev/null +++ b/src/utils.js @@ -0,0 +1,52 @@ +const vscode = require('vscode'); +const path = require('path') +const fs = require('fs') + + +function getModuleName() { + try { + let workspacePath = vscode.workspace.workspaceFolders[0].uri.path + let modPath = path.join(workspacePath, "go.mod") + let data = fs.readFileSync(modPath).toString() + let moduleNameReg = new RegExp("(?<=module ).*") + let moduleName =moduleNameReg.exec(data) + + return moduleName[0] + } catch (err) { + vscode.window.showInformationMessage("Could not get module name") + } +} + +function getFileContent(activeEditor) { + return activeEditor.document.getText() +} + +function getImportsRange(documentText) { + let start = 1; + let documentLines = documentText.split('\n') + for (var line of documentLines) { + if (line.includes('import (')) { + break; + } + start++; + } + + let end = start; + for (var line of documentLines.slice(start)) { + if (line.includes(')')) { + break; + } + end++; + } + + return { + end, + start, + }; +}; + + + +module.exports = { + getModuleName, getFileContent, getImportsRange +}; \ No newline at end of file