From 8984ac81f66607b29e606eb28462b4937540bbf9 Mon Sep 17 00:00:00 2001 From: Vuong <3168632+vuon9@users.noreply.github.com> Date: Tue, 8 Mar 2022 19:20:40 +0700 Subject: [PATCH 1/2] WIP: Add codelens for main func (but still implementing the cmds) --- src/goMain.ts | 4 +++ src/goMainCodelens.ts | 81 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 src/goMainCodelens.ts diff --git a/src/goMain.ts b/src/goMain.ts index 585d5f2c44..3b35ad4dde 100644 --- a/src/goMain.ts +++ b/src/goMain.ts @@ -61,6 +61,7 @@ import { GO111MODULE, goModInit, isModSupported } from './goModules'; import { playgroundCommand } from './goPlayground'; import { GoReferencesCodeLensProvider } from './goReferencesCodelens'; import { GoRunTestCodeLensProvider } from './goRunTestCodelens'; +import { GoMainCodeLensProvider } from './goMainCodelens'; import { disposeGoStatusBar, expandGoStatusBar, outputChannel, updateGoStatusBar } from './goStatus'; import { debugPrevious, @@ -210,9 +211,11 @@ If you would like additional configuration for diagnostics from gopls, please se }) ); const testCodeLensProvider = new GoRunTestCodeLensProvider(); + const mainCodeLensProvider = new GoMainCodeLensProvider(); const referencesCodeLensProvider = new GoReferencesCodeLensProvider(); ctx.subscriptions.push(vscode.languages.registerCodeLensProvider(GO_MODE, testCodeLensProvider)); + ctx.subscriptions.push(vscode.languages.registerCodeLensProvider(GO_MODE, mainCodeLensProvider)); ctx.subscriptions.push(vscode.languages.registerCodeLensProvider(GO_MODE, referencesCodeLensProvider)); // debug @@ -479,6 +482,7 @@ If you would like additional configuration for diagnostics from gopls, please se if (updatedGoConfig['enableCodeLens']) { testCodeLensProvider.setEnabled(updatedGoConfig['enableCodeLens']['runtest']); + mainCodeLensProvider.setEnabled(updatedGoConfig['enableCodeLens']['runmain']); referencesCodeLensProvider.setEnabled(updatedGoConfig['enableCodeLens']['references']); } diff --git a/src/goMainCodelens.ts b/src/goMainCodelens.ts new file mode 100644 index 0000000000..294a6b4378 --- /dev/null +++ b/src/goMainCodelens.ts @@ -0,0 +1,81 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See LICENSE in the project root for license information. + *--------------------------------------------------------*/ + +'use strict'; + +import vscode = require('vscode'); +import { CancellationToken, CodeLens, TextDocument } from 'vscode'; +import { getGoConfig } from './config'; +import { GoBaseCodeLensProvider } from './goBaseCodelens'; +import { GoDocumentSymbolProvider } from './goOutline'; +import { getBenchmarkFunctions, getTestFunctions } from './testUtils'; + +const mainFuncRegx = /^main$/u; + +export class GoMainCodeLensProvider extends GoBaseCodeLensProvider { + private readonly mainRegex = /^main.+/; + + public async provideCodeLenses(document: TextDocument, token: CancellationToken): Promise { + if (!this.enabled) { + return []; + } + const config = getGoConfig(document.uri); + const codeLensConfig = config.get<{ [key: string]: any }>('enableCodeLens'); + const codelensEnabled = codeLensConfig ? codeLensConfig['runmain'] : false; + if (!codelensEnabled || !document.fileName.match('main.go')) { + return []; + } + + const codelenses = await Promise.all([ + this.getCodeLensForMainFunc(document, token) + ]); + return ([] as CodeLens[]).concat(...codelenses); + } + + // Return the first main function + private async getMainFunc( + doc: vscode.TextDocument, + token: vscode.CancellationToken + ): Promise { + const documentSymbolProvider = new GoDocumentSymbolProvider(true); + const symbols = await documentSymbolProvider.provideDocumentSymbols(doc, token); + if (!symbols || symbols.length === 0) { + return; + } + const symbol = symbols[0]; + if (!symbol) { + return; + } + const children = symbol.children; + + return children.find(sym => sym.kind === vscode.SymbolKind.Function && mainFuncRegx.test(sym.name)); + } + + private async getCodeLensForMainFunc(document: TextDocument, token: CancellationToken): Promise { + const mainPromise = async (): Promise => { + const mainFunc = await this.getMainFunc(document, token); + if (!mainFunc) { + return []; + } + + return [ + new CodeLens(mainFunc.range, { + title: 'run', + command: 'go.main.run', + arguments: [{ functionName: mainFunc.name }] + }), + new CodeLens(mainFunc.range, { + title: 'package run', + command: 'go.main.package', + arguments: [{ functionName: mainFunc.name }] + }) + ]; + }; + + return await mainPromise(); + } +} From fa778fe1202cc3cf8935d58b53fa3639b7ff0694 Mon Sep 17 00:00:00 2001 From: Vuong <3168632+vuon9@users.noreply.github.com> Date: Wed, 16 Mar 2022 00:04:26 +0700 Subject: [PATCH 2/2] feat: add running main func code lens --- docs/settings.md | 4 ++- package.json | 13 ++++++++- src/goMain.ts | 8 +++++- src/goMainCodelens.ts | 64 +++++++++++++++++++++++++++++++++++-------- 4 files changed, 74 insertions(+), 15 deletions(-) diff --git a/docs/settings.md b/docs/settings.md index d45bb62467..16319068c8 100644 --- a/docs/settings.md +++ b/docs/settings.md @@ -210,12 +210,14 @@ Feature level setting to enable/disable code lens for references and run/debug t | --- | --- | | `references` | If true, enables the references code lens. Uses guru. Recalculates when there is change to the document followed by scrolling. Unnecessary when using the language server; use the call graph feature instead.
Default: `false` | | `runtest` | If true, enables code lens for running and debugging tests
Default: `true` | +| `runmain` | If true, enables code lens for running main func
Default: `true` | Default: ``` { "references" : false, "runtest" : true, + "runmain" : true, } ``` ### `go.formatFlags` @@ -492,7 +494,7 @@ Allowed Options: `package`, `workspace`, `off` Default: `"package"` ### `gopls` -Customize `gopls` behavior by specifying the gopls' settings in this section. For example, +Customize `gopls` behavior by specifying the gopls' settings in this section. For example, ``` "gopls" : { "build.directoryFilters": ["-node_modules"] diff --git a/package.json b/package.json index e8ee7741e1..e7c811125b 100644 --- a/package.json +++ b/package.json @@ -481,6 +481,11 @@ "command": "go.global.resetState", "title": "Go: Reset Global State", "description": "Reset keys in global state to undefined." + }, + { + "command": "go.runMain", + "title": "Go: Run main() func on file", + "description": "Run main() func on file" } ], "breakpoints": [ @@ -1579,12 +1584,18 @@ "type": "boolean", "default": true, "description": "If true, enables code lens for running and debugging tests" + }, + "runmain": { + "type": "boolean", + "default": true, + "description": "If true, enables code lens for running main func" } }, "additionalProperties": false, "default": { "references": false, - "runtest": true + "runtest": true, + "runmain": true }, "description": "Feature level setting to enable/disable code lens for references and run/debug tests", "scope": "resource" diff --git a/src/goMain.ts b/src/goMain.ts index 3b35ad4dde..aa623775c3 100644 --- a/src/goMain.ts +++ b/src/goMain.ts @@ -61,7 +61,7 @@ import { GO111MODULE, goModInit, isModSupported } from './goModules'; import { playgroundCommand } from './goPlayground'; import { GoReferencesCodeLensProvider } from './goReferencesCodelens'; import { GoRunTestCodeLensProvider } from './goRunTestCodelens'; -import { GoMainCodeLensProvider } from './goMainCodelens'; +import { GoMainCodeLensProvider, runMainFunc } from './goMainCodelens'; import { disposeGoStatusBar, expandGoStatusBar, outputChannel, updateGoStatusBar } from './goStatus'; import { debugPrevious, @@ -566,6 +566,12 @@ If you would like additional configuration for diagnostics from gopls, please se }) ); + ctx.subscriptions.push( + vscode.commands.registerCommand('go.runMain', (args) => { + runMainFunc() + }) + ) + ctx.subscriptions.push( vscode.commands.registerCommand('go.show.commands', () => { const extCommands = getExtensionCommands(); diff --git a/src/goMainCodelens.ts b/src/goMainCodelens.ts index 294a6b4378..ce375e15d2 100644 --- a/src/goMainCodelens.ts +++ b/src/goMainCodelens.ts @@ -8,16 +8,18 @@ 'use strict'; import vscode = require('vscode'); +import cp = require('child_process'); + import { CancellationToken, CodeLens, TextDocument } from 'vscode'; import { getGoConfig } from './config'; import { GoBaseCodeLensProvider } from './goBaseCodelens'; import { GoDocumentSymbolProvider } from './goOutline'; -import { getBenchmarkFunctions, getTestFunctions } from './testUtils'; - -const mainFuncRegx = /^main$/u; +import { getBinPath } from './util'; +import { envPath, getCurrentGoRoot } from './utils/pathUtils'; +import { reject } from 'lodash'; export class GoMainCodeLensProvider extends GoBaseCodeLensProvider { - private readonly mainRegex = /^main.+/; + private readonly mainRegex = /^main$/; public async provideCodeLenses(document: TextDocument, token: CancellationToken): Promise { if (!this.enabled) { @@ -52,7 +54,7 @@ export class GoMainCodeLensProvider extends GoBaseCodeLensProvider { } const children = symbol.children; - return children.find(sym => sym.kind === vscode.SymbolKind.Function && mainFuncRegx.test(sym.name)); + return children.find(sym => sym.kind === vscode.SymbolKind.Function && this.mainRegex.test(sym.name)); } private async getCodeLensForMainFunc(document: TextDocument, token: CancellationToken): Promise { @@ -64,13 +66,8 @@ export class GoMainCodeLensProvider extends GoBaseCodeLensProvider { return [ new CodeLens(mainFunc.range, { - title: 'run', - command: 'go.main.run', - arguments: [{ functionName: mainFunc.name }] - }), - new CodeLens(mainFunc.range, { - title: 'package run', - command: 'go.main.package', + title: 'run main', + command: 'go.runMain', arguments: [{ functionName: mainFunc.name }] }) ]; @@ -79,3 +76,46 @@ export class GoMainCodeLensProvider extends GoBaseCodeLensProvider { return await mainPromise(); } } + +const mainFuncOutputChannel = vscode.window.createOutputChannel('Go Main'); + +export async function runMainFunc() { + let outputChannel = mainFuncOutputChannel + const goRuntimePath = getBinPath('go'); + if (!goRuntimePath) { + vscode.window.showErrorMessage( + `Failed to run "go run ." as the "go" binary cannot be found in either GOROOT(${getCurrentGoRoot}) or PATH(${envPath})` + ); + return Promise.resolve(false); + } + + const editor = vscode.window.activeTextEditor; + const documentUri = editor ? editor.document.uri : null; + const args = ['run', documentUri.path]; + + outputChannel.clear() + outputChannel.show(true) + outputChannel.appendLine(["Running main func: ", goRuntimePath, ...args].join(' ')) + + cp.execFile( + goRuntimePath, + args, + { }, + (err, stdout, stderr) => { + try { + if (err) { + outputChannel.appendLine(err.message); + return; + } + if (stdout) { + outputChannel.append(stdout); + } + if (stderr) { + outputChannel.append(stderr); + } + } catch (e) { + reject(e); + } + } + ) +} \ No newline at end of file