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

Commit

Permalink
Add DependenciesTree view code
Browse files Browse the repository at this point in the history
Co-authored-by: Vinicius Stock <vinicius.stock@shopify.com>
  • Loading branch information
paracycle and vinistock committed Feb 1, 2024
1 parent 695803a commit f122262
Show file tree
Hide file tree
Showing 3 changed files with 163 additions and 0 deletions.
5 changes: 5 additions & 0 deletions src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ export interface ClientInterface {
state: State;
formatter: string;
serverVersion?: string;
sendRequest<T>(
method: string,
param: any,
token?: vscode.CancellationToken,
): Promise<T>;
}

export interface WorkspaceInterface {
Expand Down
154 changes: 154 additions & 0 deletions src/dependenciesTree.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import * as vscode from "vscode";

import { STATUS_EMITTER, WorkspaceInterface } from "./common";

interface DependenciesNode {
getChildren(): BundlerTreeNode[] | undefined | Thenable<BundlerTreeNode[]>;
}

type BundlerTreeNode = Dependency | GemDirectoryPath | GemFilePath;

export class DependenciesTree
implements vscode.TreeDataProvider<BundlerTreeNode>
{
private readonly _onDidChangeTreeData: vscode.EventEmitter<any> =
new vscode.EventEmitter<any>();

// eslint-disable-next-line @typescript-eslint/member-ordering
readonly onDidChangeTreeData: vscode.Event<any> =
this._onDidChangeTreeData.event;

private currentWorkspace: WorkspaceInterface | undefined;

constructor(context: vscode.ExtensionContext) {
STATUS_EMITTER.event((workspace) => {
if (!workspace) {
return;
}

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

context.subscriptions.push(
vscode.window.createTreeView("dependencies", { treeDataProvider: this }),
);
}

getTreeItem(
element: BundlerTreeNode,
): vscode.TreeItem | Thenable<vscode.TreeItem> {
return element;
}

getChildren(
element?: BundlerTreeNode | undefined,
): vscode.ProviderResult<BundlerTreeNode[]> {
if (element) {
return element.getChildren();
} else {
return this.fetchDependencies();
}
}

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

private async fetchDependencies(): Promise<BundlerTreeNode[]> {
if (!this.currentWorkspace) {
return [];
}

const resp = (await this.currentWorkspace.lspClient?.sendRequest(
"rubyLsp/workspace/dependencies",
{},
)) as [
{ name: string; version: string; path: string; dependency: boolean },
];

return resp
.sort((left, right) => {
if (left.dependency === right.dependency) {
// if the two dependencies are the same, sort by name
return left.name.localeCompare(right.name);
} else {
// otherwise, direct dependencies sort before transitive dependencies
return right.dependency ? 1 : -1;
}
})
.map((dep) => {
return new Dependency(dep.name, dep.version, vscode.Uri.file(dep.path));
});
}
}

class Dependency extends vscode.TreeItem implements DependenciesNode {
constructor(
name: string,
version: string,
public readonly resourceUri: vscode.Uri,
) {
super(`${name} (${version})`, vscode.TreeItemCollapsibleState.Collapsed);
this.contextValue = "dependency";
this.iconPath = new vscode.ThemeIcon("ruby");
}

async getChildren() {
const dir = this.resourceUri;
const entries = await vscode.workspace.fs.readDirectory(dir);

return entries.map(([name, type]) => {
if (type === vscode.FileType.Directory) {
return new GemDirectoryPath(vscode.Uri.joinPath(dir, name));
} else {
return new GemFilePath(vscode.Uri.joinPath(dir, name));
}
});
}
}

class GemDirectoryPath extends vscode.TreeItem implements DependenciesNode {
constructor(public readonly resourceUri: vscode.Uri) {
super(resourceUri, vscode.TreeItemCollapsibleState.Collapsed);
this.contextValue = "gem-directory-path";
this.description = true;

this.command = {
command: "list.toggleExpand",
title: "Toggle",
};
}

async getChildren() {
const dir = this.resourceUri;
const entries = await vscode.workspace.fs.readDirectory(dir);

return entries.map(([name, type]) => {
if (type === vscode.FileType.Directory) {
return new GemDirectoryPath(vscode.Uri.joinPath(dir, name));
} else {
return new GemFilePath(vscode.Uri.joinPath(dir, name));
}
});
}
}

class GemFilePath extends vscode.TreeItem implements DependenciesNode {
constructor(public readonly resourceUri: vscode.Uri) {
super(resourceUri, vscode.TreeItemCollapsibleState.None);
this.contextValue = "gem-file-path";
this.description = true;

this.command = {
command: "vscode.open",
title: "Open",
arguments: [resourceUri],
};
}

getChildren() {
return undefined;
}
}
4 changes: 4 additions & 0 deletions src/rubyLsp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { VersionManager } from "./ruby";
import { StatusItems } from "./status";
import { TestController } from "./testController";
import { Debugger } from "./debugger";
import { DependenciesTree } from "./dependenciesTree";

// The RubyLsp class represents an instance of the entire extension. This should only be instantiated once at the
// activation event. One instance of this class controls all of the existing workspaces, telemetry and handles all
Expand All @@ -22,6 +23,7 @@ export class RubyLsp {
private readonly statusItems: StatusItems;
private readonly testController: TestController;
private readonly debug: Debugger;
// private readonly dependenciesTree: DependenciesTree;

constructor(context: vscode.ExtensionContext) {
this.context = context;
Expand All @@ -32,6 +34,8 @@ export class RubyLsp {
this.currentActiveWorkspace.bind(this),
);
this.debug = new Debugger(context, this.workspaceResolver.bind(this));
// eslint-disable-next-line no-new
new DependenciesTree(context);
this.registerCommands(context);

this.statusItems = new StatusItems();
Expand Down

0 comments on commit f122262

Please sign in to comment.