Skip to content

Commit

Permalink
feat: Add URI handler for jumping to targets (#431)
Browse files Browse the repository at this point in the history
This feature will allow us to add links to documents, and tool output which jumps to specific targets, reducing friction and wasted time.

URI handler works such that:

vscode://bazelbuild.vscode-bazel//my/path/to/tests:target

(unfortunately github itself won't link out to non-http urls)

Will jump to said target given vscode is opened to a workspace which contains a bazel workspace with that target as the first workspaceFolder

I imagine we could handle more situations like:

vscode://bazelbuild.vscode-bazel/fspath/to/workspace//my/path/to/tests:target

or

vscode://bazelbuild.vscode-bazel/test//my/path/to/tests:target

But I imagine anything of the sort would be more controversial.
  • Loading branch information
catskul authored Feb 13, 2025
1 parent b83b99c commit 7e5ee8f
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 29 deletions.
71 changes: 43 additions & 28 deletions src/definition/bazel_goto_definition_provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,37 @@ import { blaze_query } from "../protos";
// LABEL_REGEX matches label strings, e.g. @r//x/y/z:abc
const LABEL_REGEX = /"((?:@\w+)?\/\/|(?:.+\/)?[^:]*(?::[^:]+)?)"/;

export async function targetToUri(
targetText: string,
workingDirectory: Uri,
): Promise<QueryLocation | undefined> {
const match = LABEL_REGEX.exec(targetText);

const targetName = match[1];
// don't try to process visibility targets.
if (targetName.startsWith("//visibility")) {
return null;
}

const queryResult = await new BazelQuery(
getDefaultBazelExecutablePath(),
workingDirectory.fsPath,
).queryTargets(`kind(rule, "${targetName}") + kind(file, "${targetName}")`);

if (!queryResult.target.length) {
return null;
}
const result = queryResult.target[0];
let location;
if (result.type === blaze_query.Target.Discriminator.RULE) {
location = new QueryLocation(result.rule.location);
} else {
location = new QueryLocation(result.sourceFile.location);
}

return location;
}

export class BazelGotoDefinitionProvider implements DefinitionProvider {
public async provideDefinition(
document: TextDocument,
Expand All @@ -41,35 +72,19 @@ export class BazelGotoDefinitionProvider implements DefinitionProvider {

const range = document.getWordRangeAtPosition(position, LABEL_REGEX);
const targetText = document.getText(range);
const match = LABEL_REGEX.exec(targetText);

const targetName = match[1];
// don't try to process visibility targets.
if (targetName.startsWith("//visibility")) {
return null;
}

const queryResult = await new BazelQuery(
getDefaultBazelExecutablePath(),
Utils.dirname(document.uri).fsPath,
).queryTargets(`kind(rule, "${targetName}") + kind(file, "${targetName}")`);
const location = await targetToUri(targetText, Utils.dirname(document.uri));

if (!queryResult.target.length) {
return null;
}
const result = queryResult.target[0];
let location;
if (result.type === blaze_query.Target.Discriminator.RULE) {
location = new QueryLocation(result.rule.location);
} else {
location = new QueryLocation(result.sourceFile.location);
}
return [
{
originSelectionRange: range,
targetUri: Uri.file(location.path),
targetRange: location.range,
},
];
return location
? [
{
originSelectionRange: range,
targetUri: Uri.file(location.path).with({
fragment: `${location.line}:${location.column}`,
}),
targetRange: location.range,
},
]
: null;
}
}
32 changes: 31 additions & 1 deletion src/extension/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ import {
} from "../buildifier";
import { BazelBuildCodeLensProvider } from "../codelens";
import { BazelCompletionItemProvider } from "../completion-provider";
import { BazelGotoDefinitionProvider } from "../definition/bazel_goto_definition_provider";
import {
BazelGotoDefinitionProvider,
targetToUri,
} from "../definition/bazel_goto_definition_provider";
import { BazelTargetSymbolProvider } from "../symbols";
import { BazelWorkspaceTreeProvider } from "../workspace-tree";
import { activateCommandVariables } from "./command_variables";
Expand Down Expand Up @@ -105,6 +108,33 @@ export async function activate(context: vscode.ExtensionContext) {
"bazel.copyTargetToClipboard",
bazelCopyTargetToClipboard,
),
// URI handler
vscode.window.registerUriHandler({
async handleUri(uri: vscode.Uri) {
try {
const workspace = vscode.workspace.workspaceFolders[0];
const quotedUriPath = `"${uri.path}"`;
const location = await targetToUri(quotedUriPath, workspace.uri);

vscode.commands
.executeCommand(
"vscode.open",
vscode.Uri.file(location.path).with({
fragment: `${location.line}:${location.column}`,
}),
)
.then(undefined, (err) => {
void vscode.window.showErrorMessage(
`Could not open file: ${location.path} Error: ${err}`,
);
});
} catch (err: any) {
void vscode.window.showErrorMessage(
`While handling URI: ${JSON.stringify(uri)} Error: ${err}`,
);
}
},
}),
// CodeLens provider for BUILD files
vscode.languages.registerCodeLensProvider(
[{ pattern: "**/BUILD" }, { pattern: "**/BUILD.bazel" }],
Expand Down

0 comments on commit 7e5ee8f

Please sign in to comment.