Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,17 @@ jobs:
run: npm run check-lint

# Required for the test cases
- uses: bazel-contrib/[email protected]
with:
bazelisk-cache: true
disk-cache: false
repository-cache: false
module-root: ${{ github.workspace }}/test/bazel_workspace

- name: Build Bazel workspace to assure it is working & warm up cache
run: bazel build //...
working-directory: ${{ github.workspace }}/test/bazel_workspace/

- name: Install system dependencies
run: sudo apt install -y binutils rustfilt

Expand Down
17 changes: 13 additions & 4 deletions src/extension/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,13 +89,22 @@ export async function activate(context: vscode.ExtensionContext) {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
vscode.commands.executeCommand("setContext", "bazel.lsp.enabled", lspEnabled);

// Create and register the tree view
const treeView = vscode.window.createTreeView("bazelWorkspace", {
treeDataProvider: workspaceTreeProvider,
showCollapseAll: true,
});
workspaceTreeProvider.setTreeView(treeView);

context.subscriptions.push(
vscode.window.registerTreeDataProvider(
"bazelWorkspace",
workspaceTreeProvider,
),
treeView,
// Commands
...activateWrapperCommands(),

// Register command to manually refresh the tree view
vscode.commands.registerCommand("bazel.workspaceTree.refresh", () => {
workspaceTreeProvider.refresh();
}),
vscode.commands.registerCommand("bazel.refreshBazelBuildTargets", () => {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
completionItemProvider?.refresh();
Expand Down
23 changes: 16 additions & 7 deletions src/workspace-tree/bazel_package_tree_item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,16 @@ export class BazelPackageTreeItem
/**
* Initializes a new tree item with the given workspace path and package path.
*
* @param workspacePath The path to the VS Code workspace folder.
* @param resources The resources for the extension.
* @param workspaceInfo The workspace information.
* @param parent The parent tree item of this item.
* @param packagePath The path to the build package that this item represents.
* @param parentPackagePath The path to the build package of the tree item
* that is this item's parent, which indicates how much of
* {@code packagePath} should be stripped for the item's label.
*/
constructor(
private readonly resources: Resources,
private readonly workspaceInfo: BazelWorkspaceInfo,
private readonly parent: IBazelTreeItem,
private readonly packagePath: string,
private readonly parentPackagePath: string,
) {}

public mightHaveChildren(): boolean {
Expand All @@ -67,21 +66,27 @@ export class BazelPackageTreeItem
return new BazelTargetTreeItem(
this.resources,
this.workspaceInfo,
this as unknown as IBazelTreeItem,
target,
);
});
return (this.directSubpackages as IBazelTreeItem[]).concat(targets);
}

public getParent(): vscode.ProviderResult<IBazelTreeItem> {
return this.parent;
}

public getLabel(): string {
// If this is a top-level package, include the leading double-slash on the
// label.
if (this.parentPackagePath.length === 0) {
const parentPackagePath = this.parent.getPackagePath();
if (parentPackagePath.length === 0) {
return `//${this.packagePath}`;
}
// Otherwise, strip off the part of the package path that came from the
// parent item (along with the slash).
return this.packagePath.substring(this.parentPackagePath.length + 1);
return this.packagePath.substring(parentPackagePath.length + 1);
}

public getIcon(): vscode.ThemeIcon {
Expand All @@ -107,4 +112,8 @@ export class BazelPackageTreeItem
workspaceInfo: this.workspaceInfo,
};
}

public getPackagePath(): string {
return this.packagePath;
}
}
12 changes: 12 additions & 0 deletions src/workspace-tree/bazel_target_tree_item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,16 @@ export class BazelTargetTreeItem
* Initializes a new tree item with the given query result representing a
* build target.
*
* @param resources The resources for the extension.
* @param workspaceInfo The workspace information.
* @param parent The parent tree item of this item.
* @param target An object representing a build target that was produced by a
* query.
*/
constructor(
private readonly resources: Resources,
private readonly workspaceInfo: BazelWorkspaceInfo,
private readonly parent: IBazelTreeItem,
private readonly target: blaze_query.ITarget,
) {}

Expand All @@ -45,6 +49,14 @@ export class BazelTargetTreeItem
return Promise.resolve([]);
}

public getParent(): vscode.ProviderResult<IBazelTreeItem> {
return this.parent;
}

public getPackagePath(): string {
return this.parent.getPackagePath();
}

public getLabel(): string {
const fullPath = this.target.rule.name;
const colonIndex = fullPath.lastIndexOf(":");
Expand Down
11 changes: 11 additions & 0 deletions src/workspace-tree/bazel_tree_item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ export interface IBazelTreeItem {
/** Returns a promise for the children of the tree item. */
getChildren(): Thenable<IBazelTreeItem[]>;

/** Returns the parent of the tree item. */
getParent(): vscode.ProviderResult<IBazelTreeItem>;

/** Returns the text label of the tree item. */
getLabel(): string;

Expand All @@ -45,6 +48,14 @@ export interface IBazelTreeItem {
*/
getTooltip(): string | undefined;

/**
* Returns the package path of the tree item.
* For workspace folders, this returns an empty string.
* For packages, this returns the path relative to the workspace root.
* For targets, this returns the path of the package that contains the target.
*/
getPackagePath(): string;

/** Returns the command that should be executed when the item is selected. */
getCommand(): vscode.Command | undefined;

Expand Down
75 changes: 68 additions & 7 deletions src/workspace-tree/bazel_workspace_folder_tree_item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ import { Resources } from "../extension/resources";

/** A tree item representing a workspace folder. */
export class BazelWorkspaceFolderTreeItem implements IBazelTreeItem {
/**
* Stores all BazelPackageTreeItems in sorted order (by path length and in descending order).
* This is used to find the most specific match for a given file path.
*/
private sortedPackageTreeItems: BazelPackageTreeItem[] = [];

/**
* Initializes a new tree item with the given workspace folder.
*
Expand All @@ -41,6 +47,10 @@ export class BazelWorkspaceFolderTreeItem implements IBazelTreeItem {
return this.getDirectoryItems();
}

public getParent(): vscode.ProviderResult<IBazelTreeItem> {
return undefined;
}

public getLabel(): string {
return this.workspaceInfo.workspaceFolder.name;
}
Expand All @@ -61,6 +71,31 @@ export class BazelWorkspaceFolderTreeItem implements IBazelTreeItem {
return "workspaceFolder";
}

public getWorkspaceInfo(): BazelWorkspaceInfo {
return this.workspaceInfo;
}

public getPackagePath(): string {
return "";
}

/**
* Finds the package that contains the given relative file path.
* Uses the presorted list of package items for efficient lookups.
* Find the first package that is a prefix of the relative path
*
* @param relativeFilePath The filepath relative to the workspace folder.
* @returns The package tree item that contains the given relative file path,
* or undefined if no such package exists.
*/
public getClosestPackageTreeItem(
relativeFilePath: string,
): BazelPackageTreeItem | undefined {
return this.sortedPackageTreeItems.find((pkg) =>
relativeFilePath.startsWith(pkg.getPackagePath()),
);
}

/**
* Recursively creates the tree items that represent packages found in a Bazel
* query.
Expand All @@ -73,16 +108,15 @@ export class BazelWorkspaceFolderTreeItem implements IBazelTreeItem {
* common prefixes should be searched.
* @param treeItems An array into which the tree items created at this level
* in the tree will be pushed.
* @param parentPackagePath The parent package path of the items being created
* by this call, which is used to trim the package prefix from labels in
* the tree items.
* @param parent The parent tree item of the items being created by this call,
* which is used to trim the package prefix from labels in the tree items.
*/
private buildPackageTree(
packagePaths: string[],
startIndex: number,
endIndex: number,
treeItems: BazelPackageTreeItem[],
parentPackagePath: string,
parent: IBazelTreeItem,
) {
// We can assume that the caller has sorted the packages, so we scan them to
// find groupings into which we should traverse more deeply. For example, if
Expand Down Expand Up @@ -128,16 +162,16 @@ export class BazelWorkspaceFolderTreeItem implements IBazelTreeItem {
const item = new BazelPackageTreeItem(
this.resources,
this.workspaceInfo,
parent,
packagePath,
parentPackagePath,
);
treeItems.push(item);
this.buildPackageTree(
packagePaths,
groupStart + 1,
groupEnd,
item.directSubpackages,
packagePath,
item,
);

// Move our index to start looking for more groups in the next iteration
Expand Down Expand Up @@ -175,7 +209,7 @@ export class BazelWorkspaceFolderTreeItem implements IBazelTreeItem {
0,
packagePaths.length,
topLevelItems,
"",
this,
);

// Now collect any targets in the directory also (this can fail since
Expand All @@ -191,10 +225,37 @@ export class BazelWorkspaceFolderTreeItem implements IBazelTreeItem {
return new BazelTargetTreeItem(
this.resources,
this.workspaceInfo,
this as unknown as IBazelTreeItem,
target,
);
});

// Cache all packages after building the tree
this.collectAndSortPackageTreeItems(topLevelItems);

return Promise.resolve((topLevelItems as IBazelTreeItem[]).concat(targets));
}

/**
* Collect, sort and store packages for later lookup
*/
private collectAndSortPackageTreeItems(items: BazelPackageTreeItem[]): void {
this.sortedPackageTreeItems = [];
this.collectAllPackageTreeItems(items);
this.sortedPackageTreeItems.sort(
(a, b) => b.getPackagePath().length - a.getPackagePath().length,
);
}

/**
* Recursively collect all children of type BazelPackageTreeItem
*/
private collectAllPackageTreeItems(items: BazelPackageTreeItem[]): void {
for (const item of items) {
this.sortedPackageTreeItems.push(item);
if (item.directSubpackages) {
this.collectAllPackageTreeItems(item.directSubpackages);
}
}
}
}
Loading
Loading