Skip to content

Commit

Permalink
Support completion of external targets
Browse files Browse the repository at this point in the history
  • Loading branch information
kon72 authored and jfirebaugh committed Dec 15, 2023
1 parent 1402ddd commit d2e2017
Showing 1 changed file with 39 additions and 14 deletions.
53 changes: 39 additions & 14 deletions src/completion-provider/bazel_completion_provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,15 @@ function getCandidateTargetFromDocumentPosition(
const linePrefix = document
.lineAt(position)
.text.substring(0, position.character);
const atIndex = linePrefix.lastIndexOf("@");
const doubleSlashIndex = linePrefix.lastIndexOf("//");
const colonIndex = linePrefix.lastIndexOf(":");
const index = doubleSlashIndex !== -1 ? doubleSlashIndex : colonIndex;
const index =
atIndex !== -1
? atIndex
: doubleSlashIndex !== -1
? doubleSlashIndex
: colonIndex;
if (index === -1) {
return undefined;
}
Expand Down Expand Up @@ -75,7 +81,7 @@ function getAbsoluteLabel(
target: string,
document: vscode.TextDocument,
): string {
if (target.startsWith("//")) {
if (target.startsWith("//") || target.startsWith("@")) {
return target;
}
const workspace = BazelWorkspaceInfo.fromDocument(document);
Expand All @@ -89,16 +95,19 @@ function getAbsoluteLabel(
return `${packageLabel}${target}`;
}

function getRepositoryName(target: string): string {
const endOfRepo = target.indexOf("//");
return endOfRepo <= 0 ? "" : target.substring(1, endOfRepo);
}

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

/**
* Returns completion items matching the given prefix.
*
* Only label started with `//` or `:` is supported at the moment.
*/
public provideCompletionItems(
public async provideCompletionItems(
document: vscode.TextDocument,
position: vscode.Position,
) {
Expand All @@ -111,13 +120,14 @@ export class BazelCompletionItemProvider
}

candidateTarget = getAbsoluteLabel(candidateTarget, document);

if (!candidateTarget.endsWith("/") && !candidateTarget.endsWith(":")) {
candidateTarget = stripLastPackageOrTargetName(candidateTarget);
}

const repo = getRepositoryName(candidateTarget);
const targets = await this.getTargetsDefinedInRepo(repo);
const completionItems = new Array<vscode.CompletionItem>();
this.targets.forEach((target) => {
targets.forEach((target) => {
if (!target.startsWith(candidateTarget)) {
return;
}
Expand All @@ -141,12 +151,27 @@ export class BazelCompletionItemProvider
* Runs a bazel query command to acquire labels of all the targets in the
* workspace.
*/
public async refresh() {
const queryTargets = await queryQuickPickTargets("kind('.* rule', ...)");
if (queryTargets.length !== 0) {
this.targets = queryTargets.map((queryTarget) => {
return queryTarget.label;
});
public async refresh(): Promise<void> {
this.targetsInRepo.clear();
await this.queryAndCacheTargets();
}

private async getTargetsDefinedInRepo(repository = ""): Promise<string[]> {
const deferred = this.targetsInRepo.get(repository);
if (deferred) {
return await deferred;
}
return await this.queryAndCacheTargets(repository);
}

private async queryAndCacheTargets(repository = ""): Promise<string[]> {
const queryTargets = async () => {
const query = `kind('.* rule', @${repository}//...)`;
const targets = await queryQuickPickTargets(query);
return targets.map((target) => target.label);
};
const deferred = queryTargets();
this.targetsInRepo.set(repository, deferred);
return await deferred;
}
}

0 comments on commit d2e2017

Please sign in to comment.