Skip to content
This repository has been archived by the owner on May 30, 2024. It is now read-only.

Commit

Permalink
Add ability to show current file in Dependencies tree view (#1019)
Browse files Browse the repository at this point in the history
* Add ability to show current file in Dependencies tree view

By watching the `onDidChangeActiveTextEditor` event, we can reveal the current file in the Dependencies tree view. To be able to do that, however, we need to implement `getParent` method on `DependenciesTree` class. This method is used by VS Code to find the parent of a given node. In our case, we want to return the parent of a given file node, which is either a `GemDirectoryNode` or a `Dependency` node, depending on whether the parent directory is the root path of a gem or not.

Moreover, when we can `TreeView.reveal` method to reveal a node, VSCode forces the tree view to be visible, which is a little jarring if the user has hidden the tree view. To avoid that, we store the current item to reveal in an instance variable, and reveal it when the tree view becomes visible again. If the tree view is already visible, we reveal the item immediately.

* Use path to determine if we're at root

* Respect auto reveal option when showing files from gems

---------

Co-authored-by: Vinicius Stock <vinicius.stock@shopify.com>
  • Loading branch information
paracycle and vinistock authored Feb 13, 2024
1 parent a70af33 commit c0ccf6c
Showing 1 changed file with 101 additions and 13 deletions.
114 changes: 101 additions & 13 deletions src/dependenciesTree.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import path from "path";

import * as vscode from "vscode";

import { STATUS_EMITTER, WorkspaceInterface } from "./common";
Expand All @@ -20,26 +22,29 @@ export class DependenciesTree

private currentWorkspace: WorkspaceInterface | undefined;
private readonly treeView: vscode.TreeView<BundlerTreeNode>;
private readonly workspaceListener: vscode.Disposable;
private readonly subscriptions: vscode.Disposable[] = [];
private gemRootFolders: Record<string, Dependency | undefined> = {};
private currentVisibleItem: GemFilePath | undefined;

constructor() {
this.treeView = vscode.window.createTreeView("dependencies", {
treeDataProvider: this,
showCollapseAll: true,
});

this.workspaceListener = STATUS_EMITTER.event((workspace) => {
if (!workspace || workspace === this.currentWorkspace) {
return;
}

this.currentWorkspace = workspace;
this.refresh();
});
this.subscriptions.push(
STATUS_EMITTER.event(this.workspaceDidChange.bind(this)),
vscode.window.onDidChangeActiveTextEditor(
this.activeEditorDidChange.bind(this),
),
this.treeView.onDidChangeVisibility(
this.treeVisibilityDidChange.bind(this),
),
);
}

dispose(): void {
this.workspaceListener.dispose();
this.subscriptions.forEach((item) => item.dispose());
this.treeView.dispose();
}

Expand All @@ -59,12 +64,86 @@ export class DependenciesTree
}
}

refresh(): void {
getParent(element: BundlerTreeNode): vscode.ProviderResult<BundlerTreeNode> {
const parentUri = vscode.Uri.joinPath(element.resourceUri, "..");
const rootPath = path.parse(parentUri.path).root;

if (parentUri.path === rootPath) {
return undefined;
}

if (element instanceof GemDirectoryPath || element instanceof GemFilePath) {
// Look up the parent in the cache of gem root folders. This allows us
// to stop the directory tree traversal at some gem root folder.
const dependency = this.gemRootFolders[parentUri.toString()];

if (dependency) {
return dependency;
} else {
return new GemDirectoryPath(parentUri);
}
} else {
return undefined;
}
}

private refresh(): void {
this.fetchDependencies();
this._onDidChangeTreeData.fire(undefined);
}

private workspaceDidChange(workspace: WorkspaceInterface | undefined): void {
if (!workspace || workspace === this.currentWorkspace) {
return;
}

this.currentWorkspace = workspace;
this.refresh();
}

private activeEditorDidChange(editor: vscode.TextEditor | undefined): void {
const uri = editor?.document.uri;

if (!uri) {
return;
}

// In case the tree view is not visible, we need to remember the current
// visible item, so that we can reveal it when the tree view becomes visible.
this.currentVisibleItem = new GemFilePath(uri);

if (this.treeView.visible) {
this.revealElement(this.currentVisibleItem);
}
}

private treeVisibilityDidChange(
event: vscode.TreeViewVisibilityChangeEvent,
): void {
if (this.currentVisibleItem && event.visible) {
this.revealElement(this.currentVisibleItem);
}
}

private revealElement(element: BundlerTreeNode): void {
const autoReveal: boolean | undefined = vscode.workspace
.getConfiguration("explorer")
.get("autoReveal");

if (autoReveal) {
this.treeView.reveal(element, {
select: true,
focus: false,
expand: true,
});
}

this.currentVisibleItem = undefined;
}

private async fetchDependencies(): Promise<BundlerTreeNode[]> {
this.gemRootFolders = {};

if (!this.currentWorkspace) {
return [];
}
Expand All @@ -76,7 +155,7 @@ export class DependenciesTree
{ name: string; version: string; path: string; dependency: boolean },
];

return resp
const dependencies = resp
.sort((left, right) => {
if (left.dependency === right.dependency) {
// if the two dependencies are the same, sort by name
Expand All @@ -87,8 +166,17 @@ export class DependenciesTree
}
})
.map((dep) => {
return new Dependency(dep.name, dep.version, vscode.Uri.file(dep.path));
const uri = vscode.Uri.file(dep.path);
const dependency = new Dependency(dep.name, dep.version, uri);
this.gemRootFolders[uri.toString()] = dependency;
return dependency;
});

dependencies.forEach((dep) => {
this.gemRootFolders[dep.resourceUri.toString()] = dep;
});

return dependencies;
}
}

Expand Down

0 comments on commit c0ccf6c

Please sign in to comment.