From b4a8b0cc23d280a774cd1272e2979385818bbef0 Mon Sep 17 00:00:00 2001 From: Adrian Vogelsgesang Date: Tue, 13 Aug 2024 23:20:10 +0200 Subject: [PATCH] refactor: Move Bazel wrapper commands into separate file (#406) This in preparation of adding a new "Measure Coverage" command, but I don't want to make the main `extension.ts` file even longer. --- src/extension/bazel_wrapper_commands.ts | 326 ++++++++++++++++++++++++ src/extension/extension.ts | 305 +--------------------- 2 files changed, 329 insertions(+), 302 deletions(-) create mode 100644 src/extension/bazel_wrapper_commands.ts diff --git a/src/extension/bazel_wrapper_commands.ts b/src/extension/bazel_wrapper_commands.ts new file mode 100644 index 00000000..32587921 --- /dev/null +++ b/src/extension/bazel_wrapper_commands.ts @@ -0,0 +1,326 @@ +// Copyright 2024 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import * as vscode from "vscode"; + +import { IBazelCommandAdapter } from "../bazel"; +import { + BazelWorkspaceInfo, + createBazelTask, + queryQuickPickPackage, + queryQuickPickTargets, +} from "../bazel"; +import { getDefaultBazelExecutablePath } from "./configuration"; + +/** + * Builds a Bazel target and streams output to the terminal. + * + * @param adapter An object that implements {@link IBazelCommandAdapter} from + * which the command's arguments will be determined. + */ +async function bazelBuildTarget(adapter: IBazelCommandAdapter | undefined) { + if (adapter === undefined) { + // If the command adapter was unspecified, it means this command is being + // invoked via the command palatte. Provide quickpick build targets for + // the user to choose from. + const quickPick = await vscode.window.showQuickPick( + queryQuickPickTargets({ query: "kind('.* rule', ...)" }), + { + canPickMany: false, + }, + ); + // If the result was undefined, the user cancelled the quick pick, so don't + // try again. + if (quickPick) { + await bazelBuildTarget(quickPick); + } + return; + } + const commandOptions = adapter.getBazelCommandOptions(); + const task = createBazelTask("build", commandOptions); + // eslint-disable-next-line @typescript-eslint/no-floating-promises + vscode.tasks.executeTask(task); +} + +/** + * Builds a Bazel target and attaches the Starlark debugger. + * + * @param adapter An object that implements {@link IBazelCommandAdapter} from + * which the command's arguments will be determined. + */ +async function bazelBuildTargetWithDebugging( + adapter: IBazelCommandAdapter | undefined, +) { + if (adapter === undefined) { + // If the command adapter was unspecified, it means this command is being + // invoked via the command palatte. Provide quickpick build targets for + // the user to choose from. + const quickPick = await vscode.window.showQuickPick( + queryQuickPickTargets({ query: "kind('.* rule', ...)" }), + { + canPickMany: false, + }, + ); + // If the result was undefined, the user cancelled the quick pick, so don't + // try again. + if (quickPick) { + // eslint-disable-next-line @typescript-eslint/no-floating-promises + bazelBuildTargetWithDebugging(quickPick); + } + return; + } + const bazelConfigCmdLine = + vscode.workspace.getConfiguration("bazel.commandLine"); + const startupOptions = bazelConfigCmdLine.get("startupOptions"); + const commandArgs = bazelConfigCmdLine.get("commandArgs"); + + const commandOptions = adapter.getBazelCommandOptions(); + + const fullArgs = commandArgs + .concat(commandOptions.targets) + .concat(commandOptions.options); + + // eslint-disable-next-line @typescript-eslint/no-floating-promises + vscode.debug.startDebugging(undefined, { + args: fullArgs, + bazelCommand: "build", + bazelExecutablePath: getDefaultBazelExecutablePath(), + bazelStartupOptions: startupOptions, + cwd: commandOptions.workspaceInfo.bazelWorkspacePath, + name: "On-demand Bazel Build Debug", + request: "launch", + type: "bazel-launch-build", + }); +} + +/** + * Builds a Bazel package and streams output to the terminal. + * + * @param adapter An object that implements {@link IBazelCommandAdapter} from + * which the command's arguments will be determined. + */ +async function bazelbuildAll(adapter: IBazelCommandAdapter | undefined) { + await buildPackage(":all", adapter); +} + +/** + * Builds a Bazel package recursively and streams output to the terminal. + * + * @param adapter An object that implements {@link IBazelCommandAdapter} from + * which the command's arguments will be determined. + */ +async function bazelbuildAllRecursive( + adapter: IBazelCommandAdapter | undefined, +) { + await buildPackage("/...", adapter); +} + +async function buildPackage( + suffix: string, + adapter: IBazelCommandAdapter | undefined, +) { + if (adapter === undefined) { + // If the command adapter was unspecified, it means this command is being + // invoked via the command palatte. Provide quickpick build targets for + // the user to choose from. + const quickPick = await vscode.window.showQuickPick( + queryQuickPickPackage({}), + { + canPickMany: false, + }, + ); + // If the result was undefined, the user cancelled the quick pick, so don't + // try again. + if (quickPick) { + await buildPackage(suffix, quickPick); + } + return; + } + const commandOptions = adapter.getBazelCommandOptions(); + const allCommandOptions = { + options: commandOptions.options, + targets: commandOptions.targets.map((s) => s + suffix), + workspaceInfo: commandOptions.workspaceInfo, + }; + const task = createBazelTask("build", allCommandOptions); + // eslint-disable-next-line @typescript-eslint/no-floating-promises + vscode.tasks.executeTask(task); +} + +/** + * Runs a Bazel target and streams output to the terminal. + * + * @param adapter An object that implements {@link IBazelCommandAdapter} from + * which the command's arguments will be determined. + */ +async function bazelRunTarget(adapter: IBazelCommandAdapter | undefined) { + if (adapter === undefined) { + // If the command adapter was unspecified, it means this command is being + // invoked via the command palatte. Provide quickpick test targets for + // the user to choose from. + const quickPick = await vscode.window.showQuickPick( + queryQuickPickTargets({ query: "kind('.* rule', ...)" }), + { + canPickMany: false, + }, + ); + // If the result was undefined, the user cancelled the quick pick, so don't + // try again. + if (quickPick) { + await bazelRunTarget(quickPick); + } + return; + } + const commandOptions = adapter.getBazelCommandOptions(); + const task = createBazelTask("run", commandOptions); + // eslint-disable-next-line @typescript-eslint/no-floating-promises + vscode.tasks.executeTask(task); +} + +/** + * Tests a Bazel target and streams output to the terminal. + * + * @param adapter An object that implements {@link IBazelCommandAdapter} from + * which the command's arguments will be determined. + */ +async function bazelTestTarget(adapter: IBazelCommandAdapter | undefined) { + if (adapter === undefined) { + // If the command adapter was unspecified, it means this command is being + // invoked via the command palatte. Provide quickpick test targets for + // the user to choose from. + const quickPick = await vscode.window.showQuickPick( + queryQuickPickTargets({ query: "kind('.*_test rule', ...)" }), + { + canPickMany: false, + }, + ); + // If the result was undefined, the user cancelled the quick pick, so don't + // try again. + if (quickPick) { + await bazelTestTarget(quickPick); + } + return; + } + const commandOptions = adapter.getBazelCommandOptions(); + const task = createBazelTask("test", commandOptions); + // eslint-disable-next-line @typescript-eslint/no-floating-promises + vscode.tasks.executeTask(task); +} + +/** + * Tests a Bazel package and streams output to the terminal. + * + * @param adapter An object that implements {@link IBazelCommandAdapter} from + * which the command's arguments will be determined. + */ +async function bazelTestAll(adapter: IBazelCommandAdapter | undefined) { + await testPackage(":all", adapter); +} + +/** + * Tests a Bazel package recursively and streams output to the terminal. + * + * @param adapter An object that implements {@link IBazelCommandAdapter} from + * which the command's arguments will be determined. + */ +async function bazelTestAllRecursive( + adapter: IBazelCommandAdapter | undefined, +) { + await testPackage("/...", adapter); +} + +async function testPackage( + suffix: string, + adapter: IBazelCommandAdapter | undefined, +) { + if (adapter === undefined) { + // If the command adapter was unspecified, it means this command is being + // invoked via the command palatte. Provide quickpick build targets for + // the user to choose from. + const quickPick = await vscode.window.showQuickPick( + queryQuickPickPackage({}), + { + canPickMany: false, + }, + ); + // If the result was undefined, the user cancelled the quick pick, so don't + // try again. + if (quickPick) { + await testPackage(suffix, quickPick); + } + return; + } + const commandOptions = adapter.getBazelCommandOptions(); + const allCommandOptions = { + options: commandOptions.options, + targets: commandOptions.targets.map((s) => s + suffix), + workspaceInfo: commandOptions.workspaceInfo, + }; + const task = createBazelTask("test", allCommandOptions); + // eslint-disable-next-line @typescript-eslint/no-floating-promises + vscode.tasks.executeTask(task); +} + +/** + * Cleans a Bazel workspace. + * + * If there is only a single workspace open, it will be cleaned immediately. If + * there are multiple workspace folders open, a quick-pick window will be opened + * asking the user to choose one. + */ +async function bazelClean() { + const workspaceInfo = await BazelWorkspaceInfo.fromWorkspaceFolders(); + if (!workspaceInfo) { + // eslint-disable-next-line @typescript-eslint/no-floating-promises + vscode.window.showInformationMessage( + "Please open a Bazel workspace folder to use this command.", + ); + + return; + } + const task = createBazelTask("clean", { + options: [], + targets: [], + workspaceInfo, + }); + // eslint-disable-next-line @typescript-eslint/no-floating-promises + vscode.tasks.executeTask(task); +} + +/** + * Activate all user-facing commands which simply wrap Bazel commands + * such as `build`, `clean`, etc. + */ +export function activateWrapperCommands(): vscode.Disposable[] { + return [ + vscode.commands.registerCommand("bazel.buildTarget", bazelBuildTarget), + vscode.commands.registerCommand( + "bazel.buildTargetWithDebugging", + bazelBuildTargetWithDebugging, + ), + vscode.commands.registerCommand("bazel.buildAll", bazelbuildAll), + vscode.commands.registerCommand( + "bazel.buildAllRecursive", + bazelbuildAllRecursive, + ), + vscode.commands.registerCommand("bazel.runTarget", bazelRunTarget), + vscode.commands.registerCommand("bazel.testTarget", bazelTestTarget), + vscode.commands.registerCommand("bazel.testAll", bazelTestAll), + vscode.commands.registerCommand( + "bazel.testAllRecursive", + bazelTestAllRecursive, + ), + vscode.commands.registerCommand("bazel.clean", bazelClean), + ]; +} diff --git a/src/extension/extension.ts b/src/extension/extension.ts index d2bcd095..a4c273e1 100644 --- a/src/extension/extension.ts +++ b/src/extension/extension.ts @@ -19,14 +19,7 @@ import { ServerOptions, } from "vscode-languageclient/node"; -import { - BazelWorkspaceInfo, - activateTaskProvider, - createBazelTask, - queryQuickPickPackage, - queryQuickPickTargets, -} from "../bazel"; -import { IBazelCommandAdapter } from "../bazel"; +import { activateTaskProvider, IBazelCommandAdapter } from "../bazel"; import { BuildifierDiagnosticsManager, BuildifierFormatProvider, @@ -37,9 +30,9 @@ import { BazelCompletionItemProvider } from "../completion-provider"; import { BazelGotoDefinitionProvider } from "../definition/bazel_goto_definition_provider"; import { BazelTargetSymbolProvider } from "../symbols"; import { BazelWorkspaceTreeProvider } from "../workspace-tree"; -import { getDefaultBazelExecutablePath } from "./configuration"; import { activateCommandVariables } from "./command_variables"; import { activateTesting } from "../test-explorer"; +import { activateWrapperCommands } from "./bazel_wrapper_commands"; /** * Called when the extension is activated; that is, when its first command is @@ -99,24 +92,7 @@ export async function activate(context: vscode.ExtensionContext) { workspaceTreeProvider, ), // Commands - vscode.commands.registerCommand("bazel.buildTarget", bazelBuildTarget), - vscode.commands.registerCommand( - "bazel.buildTargetWithDebugging", - bazelBuildTargetWithDebugging, - ), - vscode.commands.registerCommand("bazel.buildAll", bazelbuildAll), - vscode.commands.registerCommand( - "bazel.buildAllRecursive", - bazelbuildAllRecursive, - ), - vscode.commands.registerCommand("bazel.runTarget", bazelRunTarget), - vscode.commands.registerCommand("bazel.testTarget", bazelTestTarget), - vscode.commands.registerCommand("bazel.testAll", bazelTestAll), - vscode.commands.registerCommand( - "bazel.testAllRecursive", - bazelTestAllRecursive, - ), - vscode.commands.registerCommand("bazel.clean", bazelClean), + ...activateWrapperCommands(), vscode.commands.registerCommand("bazel.refreshBazelBuildTargets", () => { // eslint-disable-next-line @typescript-eslint/no-floating-promises completionItemProvider.refresh(); @@ -185,281 +161,6 @@ function createLsp(config: vscode.WorkspaceConfiguration) { return new LanguageClient("Bazel LSP Client", serverOptions, clientOptions); } -/** - * Builds a Bazel target and streams output to the terminal. - * - * @param adapter An object that implements {@link IBazelCommandAdapter} from - * which the command's arguments will be determined. - */ -async function bazelBuildTarget(adapter: IBazelCommandAdapter | undefined) { - if (adapter === undefined) { - // If the command adapter was unspecified, it means this command is being - // invoked via the command palatte. Provide quickpick build targets for - // the user to choose from. - const quickPick = await vscode.window.showQuickPick( - queryQuickPickTargets({ query: "kind('.* rule', ...)" }), - { - canPickMany: false, - }, - ); - // If the result was undefined, the user cancelled the quick pick, so don't - // try again. - if (quickPick) { - await bazelBuildTarget(quickPick); - } - return; - } - const commandOptions = adapter.getBazelCommandOptions(); - const task = createBazelTask("build", commandOptions); - // eslint-disable-next-line @typescript-eslint/no-floating-promises - vscode.tasks.executeTask(task); -} - -/** - * Builds a Bazel target and attaches the Starlark debugger. - * - * @param adapter An object that implements {@link IBazelCommandAdapter} from - * which the command's arguments will be determined. - */ -async function bazelBuildTargetWithDebugging( - adapter: IBazelCommandAdapter | undefined, -) { - if (adapter === undefined) { - // If the command adapter was unspecified, it means this command is being - // invoked via the command palatte. Provide quickpick build targets for - // the user to choose from. - const quickPick = await vscode.window.showQuickPick( - queryQuickPickTargets({ query: "kind('.* rule', ...)" }), - { - canPickMany: false, - }, - ); - // If the result was undefined, the user cancelled the quick pick, so don't - // try again. - if (quickPick) { - // eslint-disable-next-line @typescript-eslint/no-floating-promises - bazelBuildTargetWithDebugging(quickPick); - } - return; - } - const bazelConfigCmdLine = - vscode.workspace.getConfiguration("bazel.commandLine"); - const startupOptions = bazelConfigCmdLine.get("startupOptions"); - const commandArgs = bazelConfigCmdLine.get("commandArgs"); - - const commandOptions = adapter.getBazelCommandOptions(); - - const fullArgs = commandArgs - .concat(commandOptions.targets) - .concat(commandOptions.options); - - // eslint-disable-next-line @typescript-eslint/no-floating-promises - vscode.debug.startDebugging(undefined, { - args: fullArgs, - bazelCommand: "build", - bazelExecutablePath: getDefaultBazelExecutablePath(), - bazelStartupOptions: startupOptions, - cwd: commandOptions.workspaceInfo.bazelWorkspacePath, - name: "On-demand Bazel Build Debug", - request: "launch", - type: "bazel-launch-build", - }); -} - -/** - * Builds a Bazel package and streams output to the terminal. - * - * @param adapter An object that implements {@link IBazelCommandAdapter} from - * which the command's arguments will be determined. - */ -async function bazelbuildAll(adapter: IBazelCommandAdapter | undefined) { - await buildPackage(":all", adapter); -} - -/** - * Builds a Bazel package recursively and streams output to the terminal. - * - * @param adapter An object that implements {@link IBazelCommandAdapter} from - * which the command's arguments will be determined. - */ -async function bazelbuildAllRecursive( - adapter: IBazelCommandAdapter | undefined, -) { - await buildPackage("/...", adapter); -} - -async function buildPackage( - suffix: string, - adapter: IBazelCommandAdapter | undefined, -) { - if (adapter === undefined) { - // If the command adapter was unspecified, it means this command is being - // invoked via the command palatte. Provide quickpick build targets for - // the user to choose from. - const quickPick = await vscode.window.showQuickPick( - queryQuickPickPackage({}), - { - canPickMany: false, - }, - ); - // If the result was undefined, the user cancelled the quick pick, so don't - // try again. - if (quickPick) { - await buildPackage(suffix, quickPick); - } - return; - } - const commandOptions = adapter.getBazelCommandOptions(); - const allCommandOptions = { - options: commandOptions.options, - targets: commandOptions.targets.map((s) => s + suffix), - workspaceInfo: commandOptions.workspaceInfo, - }; - const task = createBazelTask("build", allCommandOptions); - // eslint-disable-next-line @typescript-eslint/no-floating-promises - vscode.tasks.executeTask(task); -} - -/** - * Runs a Bazel target and streams output to the terminal. - * - * @param adapter An object that implements {@link IBazelCommandAdapter} from - * which the command's arguments will be determined. - */ -async function bazelRunTarget(adapter: IBazelCommandAdapter | undefined) { - if (adapter === undefined) { - // If the command adapter was unspecified, it means this command is being - // invoked via the command palatte. Provide quickpick test targets for - // the user to choose from. - const quickPick = await vscode.window.showQuickPick( - queryQuickPickTargets({ query: "kind('.* rule', ...)" }), - { - canPickMany: false, - }, - ); - // If the result was undefined, the user cancelled the quick pick, so don't - // try again. - if (quickPick) { - await bazelRunTarget(quickPick); - } - return; - } - const commandOptions = adapter.getBazelCommandOptions(); - const task = createBazelTask("run", commandOptions); - // eslint-disable-next-line @typescript-eslint/no-floating-promises - vscode.tasks.executeTask(task); -} - -/** - * Tests a Bazel target and streams output to the terminal. - * - * @param adapter An object that implements {@link IBazelCommandAdapter} from - * which the command's arguments will be determined. - */ -async function bazelTestTarget(adapter: IBazelCommandAdapter | undefined) { - if (adapter === undefined) { - // If the command adapter was unspecified, it means this command is being - // invoked via the command palatte. Provide quickpick test targets for - // the user to choose from. - const quickPick = await vscode.window.showQuickPick( - queryQuickPickTargets({ query: "kind('.*_test rule', ...)" }), - { - canPickMany: false, - }, - ); - // If the result was undefined, the user cancelled the quick pick, so don't - // try again. - if (quickPick) { - await bazelTestTarget(quickPick); - } - return; - } - const commandOptions = adapter.getBazelCommandOptions(); - const task = createBazelTask("test", commandOptions); - // eslint-disable-next-line @typescript-eslint/no-floating-promises - vscode.tasks.executeTask(task); -} - -/** - * Tests a Bazel package and streams output to the terminal. - * - * @param adapter An object that implements {@link IBazelCommandAdapter} from - * which the command's arguments will be determined. - */ -async function bazelTestAll(adapter: IBazelCommandAdapter | undefined) { - await testPackage(":all", adapter); -} - -/** - * Tests a Bazel package recursively and streams output to the terminal. - * - * @param adapter An object that implements {@link IBazelCommandAdapter} from - * which the command's arguments will be determined. - */ -async function bazelTestAllRecursive( - adapter: IBazelCommandAdapter | undefined, -) { - await testPackage("/...", adapter); -} - -async function testPackage( - suffix: string, - adapter: IBazelCommandAdapter | undefined, -) { - if (adapter === undefined) { - // If the command adapter was unspecified, it means this command is being - // invoked via the command palatte. Provide quickpick build targets for - // the user to choose from. - const quickPick = await vscode.window.showQuickPick( - queryQuickPickPackage({}), - { - canPickMany: false, - }, - ); - // If the result was undefined, the user cancelled the quick pick, so don't - // try again. - if (quickPick) { - await testPackage(suffix, quickPick); - } - return; - } - const commandOptions = adapter.getBazelCommandOptions(); - const allCommandOptions = { - options: commandOptions.options, - targets: commandOptions.targets.map((s) => s + suffix), - workspaceInfo: commandOptions.workspaceInfo, - }; - const task = createBazelTask("test", allCommandOptions); - // eslint-disable-next-line @typescript-eslint/no-floating-promises - vscode.tasks.executeTask(task); -} - -/** - * Cleans a Bazel workspace. - * - * If there is only a single workspace open, it will be cleaned immediately. If - * there are multiple workspace folders open, a quick-pick window will be opened - * asking the user to choose one. - */ -async function bazelClean() { - const workspaceInfo = await BazelWorkspaceInfo.fromWorkspaceFolders(); - if (!workspaceInfo) { - // eslint-disable-next-line @typescript-eslint/no-floating-promises - vscode.window.showInformationMessage( - "Please open a Bazel workspace folder to use this command.", - ); - - return; - } - const task = createBazelTask("clean", { - options: [], - targets: [], - workspaceInfo, - }); - // eslint-disable-next-line @typescript-eslint/no-floating-promises - vscode.tasks.executeTask(task); -} - /** * Copies a target to the clipboard. */