| /*--------------------------------------------------------- | |
| * 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 { CodeLens, TextDocument } from 'vscode'; | |
| import { getGoConfig } from './config'; | |
| import { GoDocumentSymbolProvider } from './goDocumentSymbols'; | |
| import { getBenchmarkFunctions, getTestFunctions } from './testUtils'; | |
| import { GoExtensionContext } from './context'; | |
| import { GO_MODE } from './goMode'; | |
| import { experiments } from './experimental'; | |
| export class GoRunTestCodeLensProvider implements vscode.CodeLensProvider { | |
| private enabled = true; | |
| private onDidChangeCodeLensesEmitter = new vscode.EventEmitter<void>(); | |
| public get onDidChangeCodeLenses(): vscode.Event<void> { | |
| return this.onDidChangeCodeLensesEmitter.event; | |
| } | |
| public setEnabled(enabled: boolean): void { | |
| if (this.enabled !== enabled) { | |
| this.enabled = enabled; | |
| this.onDidChangeCodeLensesEmitter.fire(); | |
| } | |
| } | |
| static activate(ctx: vscode.ExtensionContext, goCtx: GoExtensionContext) { | |
| const testCodeLensProvider = new this(goCtx); | |
| const setEnabled = () => { | |
| const updatedGoConfig = getGoConfig(); | |
| if (updatedGoConfig['enableCodeLens']) { | |
| testCodeLensProvider.setEnabled( | |
| updatedGoConfig['enableCodeLens']['runtest'] && !experiments.testExplorer | |
| ); | |
| } | |
| }; | |
| ctx.subscriptions.push(vscode.languages.registerCodeLensProvider(GO_MODE, testCodeLensProvider)); | |
| ctx.subscriptions.push(experiments.onDidChange(() => setEnabled())); | |
| ctx.subscriptions.push( | |
| vscode.workspace.onDidChangeConfiguration(async (e: vscode.ConfigurationChangeEvent) => { | |
| if (e.affectsConfiguration('go')) { | |
| setEnabled(); | |
| } | |
| }) | |
| ); | |
| } | |
| constructor(private readonly goCtx: GoExtensionContext) {} | |
| private readonly benchmarkRegex = /^Benchmark.+/; | |
| public async provideCodeLenses(document: TextDocument): Promise<CodeLens[]> { | |
| if (!this.enabled) { | |
| return []; | |
| } | |
| const config = getGoConfig(document.uri); | |
| const codeLensConfig = config.get<{ [key: string]: any }>('enableCodeLens'); | |
| const codelensEnabled = codeLensConfig ? codeLensConfig['runtest'] : false; | |
| if (!codelensEnabled || !document.fileName.endsWith('_test.go')) { | |
| return []; | |
| } | |
| const codelenses = await Promise.all([ | |
| this.getCodeLensForPackage(document), | |
| this.getCodeLensForFunctions(document) | |
| ]); | |
| return ([] as CodeLens[]).concat(...codelenses); | |
| } | |
| private async getCodeLensForPackage(document: TextDocument): Promise<CodeLens[]> { | |
| const documentSymbolProvider = GoDocumentSymbolProvider(this.goCtx); | |
| const symbols = await documentSymbolProvider.provideDocumentSymbols(document); | |
| if (!symbols || symbols.length === 0) { | |
| return []; | |
| } | |
| const pkg = symbols[0]; | |
| if (!pkg) { | |
| return []; | |
| } | |
| const range = pkg.range; | |
| const packageCodeLens = [ | |
| new CodeLens(range, { | |
| title: 'run package tests', | |
| command: 'go.test.package' | |
| }), | |
| new CodeLens(range, { | |
| title: 'run file tests', | |
| command: 'go.test.file' | |
| }) | |
| ]; | |
| if (pkg.children.some((sym) => sym.kind === vscode.SymbolKind.Function && this.benchmarkRegex.test(sym.name))) { | |
| packageCodeLens.push( | |
| new CodeLens(range, { | |
| title: 'run package benchmarks', | |
| command: 'go.benchmark.package' | |
| }), | |
| new CodeLens(range, { | |
| title: 'run file benchmarks', | |
| command: 'go.benchmark.file' | |
| }) | |
| ); | |
| } | |
| return packageCodeLens; | |
| } | |
| private async getCodeLensForFunctions(document: TextDocument): Promise<CodeLens[]> { | |
| const testPromise = async (): Promise<CodeLens[]> => { | |
| const codelens: CodeLens[] = []; | |
| const testFunctions = await getTestFunctions(this.goCtx, document); | |
| if (!testFunctions) { | |
| return codelens; | |
| } | |
| const simpleRunRegex = /t.Run\("([^"]+)",/; | |
| for (const f of testFunctions) { | |
| const functionName = f.name; | |
| codelens.push( | |
| new CodeLens(f.range, { | |
| title: 'run test', | |
| command: 'go.test.cursor', | |
| arguments: [{ functionName }] | |
| }), | |
| new CodeLens(f.range, { | |
| title: 'debug test', | |
| command: 'go.debug.cursor', | |
| arguments: [{ functionName }] | |
| }) | |
| ); | |
| for (let i = f.range.start.line; i < f.range.end.line; i++) { | |
| const line = document.lineAt(i); | |
| const simpleMatch = line.text.match(simpleRunRegex); | |
| // BUG: this does not handle nested subtests. This should | |
| // be solved once codelens is handled by gopls and not by | |
| // vscode. | |
| if (simpleMatch) { | |
| const subTestName = simpleMatch[1]; | |
| codelens.push( | |
| new CodeLens(line.range, { | |
| title: 'run test', | |
| command: 'go.subtest.cursor', | |
| arguments: [{ functionName, subTestName }] | |
| }), | |
| new CodeLens(line.range, { | |
| title: 'debug test', | |
| command: 'go.debug.subtest.cursor', | |
| arguments: [{ functionName, subTestName }] | |
| }) | |
| ); | |
| } | |
| } | |
| } | |
| return codelens; | |
| }; | |
| const benchmarkPromise = async (): Promise<CodeLens[]> => { | |
| const benchmarkFunctions = await getBenchmarkFunctions(this.goCtx, document); | |
| if (!benchmarkFunctions) { | |
| return []; | |
| } | |
| const codelens: CodeLens[] = []; | |
| for (const f of benchmarkFunctions) { | |
| codelens.push( | |
| new CodeLens(f.range, { | |
| title: 'run benchmark', | |
| command: 'go.benchmark.cursor', | |
| arguments: [{ functionName: f.name }] | |
| }) | |
| ); | |
| codelens.push( | |
| new CodeLens(f.range, { | |
| title: 'debug benchmark', | |
| command: 'go.debug.cursor', | |
| arguments: [{ functionName: f.name }] | |
| }) | |
| ); | |
| } | |
| return codelens; | |
| }; | |
| const codelenses = await Promise.all([testPromise(), benchmarkPromise()]); | |
| return ([] as CodeLens[]).concat(...codelenses); | |
| } | |
| } |