Skip to content

Commit 0d67415

Browse files
authored
Merge branch 'master' into feature/353-sync-tree-view
2 parents caefaf6 + 3c48c7a commit 0d67415

File tree

9 files changed

+480
-35
lines changed

9 files changed

+480
-35
lines changed

package.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@
107107
},
108108
{
109109
"category": "Bazel",
110-
"command": "bazel.copyTargetToClipboard",
110+
"command": "bazel.copyLabelToClipboard",
111111
"title": "Copy Label to Clipboard"
112112
},
113113
{
@@ -322,8 +322,8 @@
322322
"menus": {
323323
"commandPalette": [
324324
{
325-
"command": "bazel.copyTargetToClipboard",
326-
"when": "false"
325+
"command": "bazel.copyLabelToClipboard",
326+
"when": "bazel.haveWorkspace"
327327
},
328328
{
329329
"command": "bazel.buildTarget",
@@ -422,12 +422,12 @@
422422
"group": "build"
423423
},
424424
{
425-
"command": "bazel.copyTargetToClipboard",
425+
"command": "bazel.copyLabelToClipboard",
426426
"when": "view == bazelWorkspace && viewItem == rule",
427427
"group": "build"
428428
},
429429
{
430-
"command": "bazel.copyTargetToClipboard",
430+
"command": "bazel.copyLabelToClipboard",
431431
"when": "view == bazelWorkspace && viewItem == testRule",
432432
"group": "build"
433433
}

src/codelens/bazel_build_code_lens_provider.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,16 @@ export class BazelBuildCodeLensProvider implements vscode.CodeLensProvider {
137137
countMap.set(line, countMap.has(line));
138138
return countMap;
139139
}, new Map<number, boolean>());
140-
for (const target of queryResult.target) {
140+
// Sort targets by length first, then alphabetically
141+
// This ensures shorter names (often main targets) appear first, with consistent ordering within each length group
142+
const sortedTargets = [...queryResult.target].sort((a, b) => {
143+
const lengthDiff = a.rule.name.length - b.rule.name.length;
144+
return lengthDiff !== 0
145+
? lengthDiff
146+
: a.rule.name.localeCompare(b.rule.name);
147+
});
148+
149+
for (const target of sortedTargets) {
141150
const location = new QueryLocation(target.rule.location);
142151
const targetName = target.rule.name;
143152
const ruleClass = target.rule.ruleClass;
@@ -147,7 +156,7 @@ export class BazelBuildCodeLensProvider implements vscode.CodeLensProvider {
147156

148157
// All targets support target copying and building.
149158
commands.push({
150-
commandString: "bazel.copyTargetToClipboard",
159+
commandString: "bazel.copyLabelToClipboard",
151160
name: "Copy",
152161
});
153162
commands.push({

src/extension/bazel_wrapper_commands.ts

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,16 @@
1313
// limitations under the License.
1414

1515
import * as vscode from "vscode";
16+
import * as path from "path";
1617

1718
import { IBazelCommandAdapter } from "../bazel/bazel_command";
1819
import { BazelWorkspaceInfo } from "../bazel/bazel_workspace_info";
1920
import { getDefaultBazelExecutablePath } from "./configuration";
20-
import { getBazelPackageFile } from "../bazel/bazel_utils";
21+
import {
22+
getBazelPackageFile,
23+
getBazelWorkspaceFolder,
24+
getBazelPackageFolder,
25+
} from "../bazel/bazel_utils";
2126
import {
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-zA-Z0-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
}

src/extension/extension.ts

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import * as vscode from "vscode";
1616
import * as lc from "vscode-languageclient/node";
1717

18-
import { activateTaskProvider, IBazelCommandAdapter } from "../bazel";
18+
import { activateTaskProvider } from "../bazel";
1919
import {
2020
BuildifierDiagnosticsManager,
2121
BuildifierFormatProvider,
@@ -110,10 +110,6 @@ export async function activate(context: vscode.ExtensionContext) {
110110
completionItemProvider?.refresh();
111111
workspaceTreeProvider.refresh();
112112
}),
113-
vscode.commands.registerCommand(
114-
"bazel.copyTargetToClipboard",
115-
bazelCopyTargetToClipboard,
116-
),
117113
// URI handler
118114
vscode.window.registerUriHandler({
119115
async handleUri(uri: vscode.Uri) {
@@ -213,19 +209,3 @@ function createLsp(config: vscode.WorkspaceConfiguration) {
213209
clientOptions,
214210
);
215211
}
216-
217-
/**
218-
* Copies a target to the clipboard.
219-
*/
220-
function bazelCopyTargetToClipboard(adapter: IBazelCommandAdapter | undefined) {
221-
if (adapter === undefined) {
222-
// This command should not be enabled in the commands palette, so adapter
223-
// should always be present.
224-
return;
225-
}
226-
// This can only be called on single targets, so we can assume there is only
227-
// one of them.
228-
const target = adapter.getBazelCommandOptions().targets[0];
229-
// eslint-disable-next-line @typescript-eslint/no-floating-promises
230-
vscode.env.clipboard.writeText(target);
231-
}

test/bazel_workspace/pkg1/BUILD

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,19 @@
1+
# Note this file contains different kinds of labels on purpose - used by copy_label_to_clipboard test
2+
load("@local_config_platform//:constraints.bzl", "HOST_CONSTRAINTS") # external label
3+
py_library(
4+
name="pkg1", # Same label as package
5+
srcs=glob(["subfolder/*.py"]),
6+
imports=["subfolder"],
7+
)
8+
filegroup(
9+
name="src_files",
10+
srcs=["main.py"], # relative file label
11+
)
112
py_binary(
2-
name = "main",
3-
srcs = ["main.py"],
4-
visibility = ["//visibility:public"],
13+
name="main", # name label
14+
srcs=[":src_files"], # relative target label
15+
visibility=["//visibility:public"], # Full label on purpose
16+
deps=["//pkg1"], # Short package label on purpose
517
)
618
filegroup(
719
name="foo",

test/bazel_workspace/pkg1/main.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
def main():
2-
print("Hello, world!")
1+
from lib import hello_world
32

43
if __name__ == "__main__":
5-
main()
4+
hello_world()
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
def hello_world():
2+
print("Hello, world!")

0 commit comments

Comments
 (0)