1313// limitations under the License.
1414
1515import * as vscode from "vscode" ;
16+ import * as path from "path" ;
1617
1718import { IBazelCommandAdapter } from "../bazel/bazel_command" ;
1819import { BazelWorkspaceInfo } from "../bazel/bazel_workspace_info" ;
1920import { getDefaultBazelExecutablePath } from "./configuration" ;
20- import { getBazelPackageFile } from "../bazel/bazel_utils" ;
21+ import {
22+ getBazelPackageFile ,
23+ getBazelWorkspaceFolder ,
24+ getBazelPackageFolder ,
25+ } from "../bazel/bazel_utils" ;
2126import {
2227 queryQuickPickTargets ,
2328 queryQuickPickPackage ,
@@ -368,6 +373,80 @@ async function bazelGoToLabel(target_info?: blaze_query.ITarget | undefined) {
368373 } ) ;
369374}
370375
376+ /**
377+ * Copies the Bazel label to the clipboard.
378+ *
379+ * If no adapter is provided, it will find the label under the cursor in the
380+ * active editor, validate it, and copy it to the clipboard. If the label is a
381+ * short form (missing the target name), it will be expanded to the full label.
382+ */
383+ function bazelCopyLabelToClipboard ( adapter : IBazelCommandAdapter | undefined ) {
384+ let label : string ;
385+
386+ if ( adapter !== undefined ) {
387+ // Called from a command adapter, so we can assume there is only one target.
388+ label = adapter . getBazelCommandOptions ( ) . targets [ 0 ] ;
389+ } else {
390+ // Called from command palette
391+ const editor = vscode . window . activeTextEditor ;
392+ if ( ! editor ) {
393+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
394+ vscode . window . showInformationMessage (
395+ "Please open a file to copy a label from." ,
396+ ) ;
397+ return ;
398+ }
399+
400+ const document = editor . document ;
401+ const position = editor . selection . active ;
402+ const wordRange = document . getWordRangeAtPosition (
403+ position ,
404+ / (?< ! [ ^ " ' ] ) [ a - z A - Z 0 - 9 _ / : . - @ ] + (? ! [ ^ " ' ] ) / ,
405+ ) ;
406+
407+ if ( ! wordRange ) {
408+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
409+ vscode . window . showInformationMessage (
410+ "No label found at cursor position." ,
411+ ) ;
412+ return ;
413+ }
414+
415+ label = document . getText ( wordRange ) ;
416+
417+ // If the label doesn't start with //, prepend the current package
418+ if ( ! label . startsWith ( "//" ) && ! label . startsWith ( "@" ) ) {
419+ const filePath = document . uri . fsPath ;
420+ const packagePath = getBazelPackageFolder ( filePath ) ;
421+ if ( ! packagePath ) {
422+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
423+ vscode . window . showErrorMessage ( "Not in a Bazel package." ) ;
424+ return ;
425+ }
426+
427+ // Get the package relative to workspace
428+ const workspaceRoot = getBazelWorkspaceFolder ( filePath ) ;
429+ const relativePackage = path . relative ( workspaceRoot , packagePath ) || "." ;
430+ label = `//${ relativePackage } ${ label . startsWith ( ":" ) ? "" : ":" } ${ label } ` ;
431+ }
432+
433+ // Handle the case where the target name is omitted
434+ // (e.g., "//foo/bar" instead of "//foo/bar:bar")
435+ if ( ! label . includes ( ":" ) ) {
436+ const parts = label . split ( "/" ) ;
437+ const lastPart = parts [ parts . length - 1 ] ;
438+ if ( lastPart && lastPart !== "..." ) {
439+ // Don't expand "//..."
440+ label = `${ label } :${ lastPart } ` ;
441+ }
442+ }
443+ }
444+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
445+ vscode . env . clipboard . writeText ( label ) ;
446+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
447+ vscode . window . showInformationMessage ( `Copied to clipboard: ${ label } ` ) ;
448+ }
449+
371450/**
372451 * Activate all user-facing commands which simply wrap Bazel commands
373452 * such as `build`, `clean`, etc.
@@ -394,5 +473,9 @@ export function activateWrapperCommands(): vscode.Disposable[] {
394473 vscode . commands . registerCommand ( "bazel.clean" , bazelClean ) ,
395474 vscode . commands . registerCommand ( "bazel.goToBuildFile" , bazelGoToBuildFile ) ,
396475 vscode . commands . registerCommand ( "bazel.goToLabel" , bazelGoToLabel ) ,
476+ vscode . commands . registerCommand (
477+ "bazel.copyLabelToClipboard" ,
478+ bazelCopyLabelToClipboard ,
479+ ) ,
397480 ] ;
398481}
0 commit comments