Skip to content

Commit

Permalink
Support completion of repository names
Browse files Browse the repository at this point in the history
  • Loading branch information
kon72 authored and jfirebaugh committed Dec 15, 2023
1 parent d2e2017 commit c61ae4e
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 7 deletions.
87 changes: 87 additions & 0 deletions src/completion-provider/bazel_repository_completion_provider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// Copyright 2023 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 { queryQuickPickTargets } from "../bazel";

function isCompletingInsideRepositoryLabel(
document: vscode.TextDocument,
position: vscode.Position,
) {
const linePrefix = document
.lineAt(position)
.text.substring(0, position.character);
const startOfRepo = linePrefix.lastIndexOf("@");
const endOfRepo = linePrefix.lastIndexOf("//");
return startOfRepo !== -1 && (endOfRepo === -1 || endOfRepo < startOfRepo);
}

function getTargetName(label: string) {
const colonIndex = label.lastIndexOf(":");
if (colonIndex === -1) {
return undefined;
}
return label.substring(colonIndex + 1);
}

export class BazelRepositoryCompletionItemProvider
implements vscode.CompletionItemProvider {
private repositories?: Promise<string[]>;

/**
* Returns completion items matching the given prefix.
*/
public async provideCompletionItems(
document: vscode.TextDocument,
position: vscode.Position,
) {
if (!isCompletingInsideRepositoryLabel(document, position)) {
return [];
}

const repos = await this.getRepos();
const completionItems = repos.map(
(repo) =>
new vscode.CompletionItem(repo, vscode.CompletionItemKind.Folder),
);
return completionItems;
}

/**
* Runs a bazel query command to acquire all the repositories in the
* workspace.
*/
public async refresh(): Promise<void> {
await this.queryAndCacheRepos();
}

private async getRepos(): Promise<string[]> {
if (this.repositories) {
return await this.repositories;
}
return await this.queryAndCacheRepos();
}

private async queryAndCacheRepos(): Promise<string[]> {
const queryRepos = async () => {
const targets = await queryQuickPickTargets(
"kind('.* rule', //external:*)",
);
return targets.map((target) => getTargetName(target.label));
};
const deferred = queryRepos();
this.repositories = deferred;
return await deferred;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ function getRepositoryName(target: string): string {
return endOfRepo <= 0 ? "" : target.substring(1, endOfRepo);
}

export class BazelCompletionItemProvider
export class BazelTargetCompletionItemProvider
implements vscode.CompletionItemProvider {
private readonly targetsInRepo = new Map<string, Promise<string[]>>();

Expand Down
3 changes: 2 additions & 1 deletion src/completion-provider/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@
// See the License for the specific language governing permissions and
// limitations under the License.

export * from "./bazel_completion_provider";
export * from "./bazel_repository_completion_provider";
export * from "./bazel_target_completion_provider";
24 changes: 19 additions & 5 deletions src/extension/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@ import {
checkBuildifierIsAvailable,
} from "../buildifier";
import { BazelBuildCodeLensProvider } from "../codelens";
import { BazelCompletionItemProvider } from "../completion-provider";
import {
BazelRepositoryCompletionItemProvider,
BazelTargetCompletionItemProvider,
} from "../completion-provider";
import { BazelGotoDefinitionProvider } from "../definition/bazel_goto_definition_provider";
import { BazelTargetSymbolProvider } from "../symbols";
import { BazelWorkspaceTreeProvider } from "../workspace-tree";
Expand All @@ -51,15 +54,24 @@ export function activate(context: vscode.ExtensionContext) {
const workspaceTreeProvider = new BazelWorkspaceTreeProvider(context);
const codeLensProvider = new BazelBuildCodeLensProvider(context);
const buildifierDiagnostics = new BuildifierDiagnosticsManager();
const completionItemProvider = new BazelCompletionItemProvider();
const repositoryCompletionItemProvider =
new BazelRepositoryCompletionItemProvider();
const targetCompletionItemProvider = new BazelTargetCompletionItemProvider();

// tslint:disable-next-line:no-floating-promises
completionItemProvider.refresh();
repositoryCompletionItemProvider.refresh();
// tslint:disable-next-line:no-floating-promises
targetCompletionItemProvider.refresh();

context.subscriptions.push(
vscode.languages.registerCompletionItemProvider(
[{ pattern: "**/BUILD" }, { pattern: "**/BUILD.bazel" }],
completionItemProvider,
repositoryCompletionItemProvider,
"@",
),
vscode.languages.registerCompletionItemProvider(
[{ pattern: "**/BUILD" }, { pattern: "**/BUILD.bazel" }],
targetCompletionItemProvider,
"/",
":",
),
Expand Down Expand Up @@ -88,7 +100,9 @@ export function activate(context: vscode.ExtensionContext) {
vscode.commands.registerCommand("bazel.clean", bazelClean),
vscode.commands.registerCommand("bazel.refreshBazelBuildTargets", () => {
// tslint:disable-next-line:no-floating-promises
completionItemProvider.refresh();
repositoryCompletionItemProvider.refresh();
// tslint:disable-next-line:no-floating-promises
targetCompletionItemProvider.refresh();
workspaceTreeProvider.refresh();
}),
vscode.commands.registerCommand(
Expand Down

0 comments on commit c61ae4e

Please sign in to comment.