From f48ad817b62224b1e218acda154ad644235f3109 Mon Sep 17 00:00:00 2001 From: Wang Lun Date: Sat, 30 Jul 2022 13:54:51 +0900 Subject: [PATCH 1/7] fixed read dependencies from workspace-state.json --- src/SwiftPackage.ts | 12 ++- src/ui/PackageDependencyProvider.ts | 115 ++++++++++++++++++++-------- 2 files changed, 95 insertions(+), 32 deletions(-) diff --git a/src/SwiftPackage.ts b/src/SwiftPackage.ts index 8ad820312..4c9c1c900 100644 --- a/src/SwiftPackage.ts +++ b/src/SwiftPackage.ts @@ -117,9 +117,19 @@ export interface WorkspaceState { version: number; } +/** revision + (branch || version) + * ref: https://github.com/apple/swift-package-manager/blob/e25a590dc455baa430f2ec97eacc30257c172be2/Sources/Workspace/CheckoutState.swift#L19:L23 + */ +export interface CheckoutState { + revision: string; + branch: string | null; + version: string | null; +} + export interface WorkspaceStateDependency { packageRef: { identity: string; kind: string; location: string; name: string }; - state: { name: string; path?: string }; + state: { name: string; path?: string; checkoutState?: CheckoutState }; + subpath: string; } export interface PackagePlugin { diff --git a/src/ui/PackageDependencyProvider.ts b/src/ui/PackageDependencyProvider.ts index ab7877197..3bd497de7 100644 --- a/src/ui/PackageDependencyProvider.ts +++ b/src/ui/PackageDependencyProvider.ts @@ -21,7 +21,7 @@ import { WorkspaceContext } from "../WorkspaceContext"; import { FolderEvent } from "../WorkspaceContext"; import { FolderContext } from "../FolderContext"; import contextKeys from "../contextKeys"; -import { WorkspaceState } from "../SwiftPackage"; +import { WorkspaceState, WorkspaceStateDependency } from "../SwiftPackage"; /** * References: @@ -148,39 +148,24 @@ export class PackageDependenciesProvider implements vscode.TreeDataProvider item.name === child.name); - if (!editedVersion) { - uneditedChildren.push(child); - } - } - return [...uneditedChildren, ...editedChildren].sort((first, second) => - first.name.localeCompare(second.name) - ); + return this.getDependencyGraph(workspaceState, folderContext.folder.fsPath); } - const buildDirectory = buildDirectoryFromWorkspacePath(folderContext.folder.fsPath, true); + return this.getNodesInDirectory(element.path); + } - if (element instanceof PackageNode) { - // Read the contents of a package. - const packagePath = - element.type === "remote" - ? path.join(buildDirectory, "checkouts", getRepositoryName(element.path)) - : element.path; - return this.getNodesInDirectory(packagePath); - } else { - // Read the contents of a directory within a package. - return this.getNodesInDirectory(element.path); - } + private getDependencyGraph( + workspaceState: WorkspaceState | undefined, + folderContext: string + ): PackageNode[] { + return ( + workspaceState?.object.dependencies.map(dependency => { + const type = this.dependencyType(dependency); + const version = this.dependencyDisplayVersion(dependency); + const packagePath = this.dependencyPackagePath(dependency, folderContext); + return new PackageNode(dependency.packageRef.identity, packagePath, version, type); + }) ?? [] + ); } /** @@ -277,4 +262,72 @@ export class PackageDependenciesProvider implements vscode.TreeDataProvider Date: Sat, 30 Jul 2022 14:15:17 +0900 Subject: [PATCH 2/7] clean unused import --- src/ui/PackageDependencyProvider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/PackageDependencyProvider.ts b/src/ui/PackageDependencyProvider.ts index 3bd497de7..78d20574f 100644 --- a/src/ui/PackageDependencyProvider.ts +++ b/src/ui/PackageDependencyProvider.ts @@ -16,7 +16,7 @@ import * as vscode from "vscode"; import * as fs from "fs/promises"; import * as path from "path"; import configuration from "../configuration"; -import { getRepositoryName, buildDirectoryFromWorkspacePath } from "../utilities/utilities"; +import { buildDirectoryFromWorkspacePath } from "../utilities/utilities"; import { WorkspaceContext } from "../WorkspaceContext"; import { FolderEvent } from "../WorkspaceContext"; import { FolderContext } from "../FolderContext"; From 0ec0c404eb74f88971bec622dc65f5cfedc74b60 Mon Sep 17 00:00:00 2001 From: Wang Lun Date: Sun, 31 Jul 2022 02:30:29 +0900 Subject: [PATCH 3/7] add location for PackageNode and fix `View repository` --- src/commands.ts | 2 +- src/ui/PackageDependencyProvider.ts | 21 +++++++++++++++++---- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/commands.ts b/src/commands.ts index cb9425f2c..8d6f230e1 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -421,7 +421,7 @@ async function executeTaskWithUI( */ function openInExternalEditor(packageNode: PackageNode) { try { - const uri = vscode.Uri.parse(packageNode.path, true); + const uri = vscode.Uri.parse(packageNode.location, true); vscode.env.openExternal(uri); } catch { // ignore error diff --git a/src/ui/PackageDependencyProvider.ts b/src/ui/PackageDependencyProvider.ts index 78d20574f..7a6fa55ec 100644 --- a/src/ui/PackageDependencyProvider.ts +++ b/src/ui/PackageDependencyProvider.ts @@ -41,6 +41,7 @@ export class PackageNode { constructor( public name: string, public path: string, + public location: string, public version: string, public type: "local" | "remote" | "editing" ) {} @@ -163,7 +164,14 @@ export class PackageDependenciesProvider implements vscode.TreeDataProvider Date: Sun, 31 Jul 2022 02:57:14 +0900 Subject: [PATCH 4/7] fixed editing package can `view repository` --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e523706ec..b047629f0 100644 --- a/package.json +++ b/package.json @@ -310,7 +310,7 @@ }, { "command": "swift.openExternal", - "when": "view == packageDependencies && viewItem == remote" + "when": "view == packageDependencies && viewItem != local" } ] }, From a29a4cee94d96247577eee265f2453dee3d7f7fc Mon Sep 17 00:00:00 2001 From: Wang Lun Date: Mon, 1 Aug 2022 22:42:03 +0900 Subject: [PATCH 5/7] traverse local dependencies --- src/ui/PackageDependencyProvider.ts | 142 ++++++++++++++++++++++++---- 1 file changed, 124 insertions(+), 18 deletions(-) diff --git a/src/ui/PackageDependencyProvider.ts b/src/ui/PackageDependencyProvider.ts index 7a6fa55ec..f47f98d88 100644 --- a/src/ui/PackageDependencyProvider.ts +++ b/src/ui/PackageDependencyProvider.ts @@ -21,7 +21,13 @@ import { WorkspaceContext } from "../WorkspaceContext"; import { FolderEvent } from "../WorkspaceContext"; import { FolderContext } from "../FolderContext"; import contextKeys from "../contextKeys"; -import { WorkspaceState, WorkspaceStateDependency } from "../SwiftPackage"; +import { + Dependency, + PackageContents, + SwiftPackage, + WorkspaceState, + WorkspaceStateDependency, +} from "../SwiftPackage"; /** * References: @@ -149,31 +155,131 @@ export class PackageDependenciesProvider implements vscode.TreeDataProvider { + if (!workspaceState) { + return []; + } + const inUseDependencies = await this.getInUseDependencies(workspaceState, folderContext); return ( - workspaceState?.object.dependencies.map(dependency => { - const type = this.dependencyType(dependency); - const version = this.dependencyDisplayVersion(dependency); - const packagePath = this.dependencyPackagePath(dependency, folderContext); - const location = dependency.packageRef.location; - return new PackageNode( - dependency.packageRef.identity, - packagePath, - location, - version, - type - ); - }) ?? [] + workspaceState?.object.dependencies + .filter(dependency => inUseDependencies.has(dependency.packageRef.identity)) + .map(dependency => { + const type = this.dependencyType(dependency); + const version = this.dependencyDisplayVersion(dependency); + const packagePath = this.dependencyPackagePath( + dependency, + folderContext.folder.fsPath + ); + const location = dependency.packageRef.location; + return new PackageNode( + dependency.packageRef.identity, + packagePath, + location, + version, + type + ); + }) ?? [] + ); + } + + /** + * * Returns a set of all dependencies that are in use in the workspace. + * 1. local/remote/edited dependency has remote/edited dependencies, Package.resolved covers them + * 2. remote/edited dependency has a local dependency, the local dependency must have been declared in root Package.swift + * 3. local dependency has a local dependency, traverse it and find the local dependencies only recursively + * 4. pins include all remote and edited packages for 1, 2 + */ + private async getInUseDependencies( + workspaceState: WorkspaceState, + folderContext: FolderContext + ): Promise> { + const localDependencies = await this.getAllLocalDependencySet( + workspaceState, + folderContext ); + const remoteDependencies = this.getRemoteDependencySet(folderContext); + + // merge the two sets of dependencies + const inUseDependencies = new Set(); + localDependencies.forEach(dependency => inUseDependencies.add(dependency)); + remoteDependencies.forEach(dependency => inUseDependencies.add(dependency)); + return inUseDependencies; + } + + private getRemoteDependencySet(folderContext: FolderContext | undefined): Set { + return new Set(folderContext?.swiftPackage.resolved?.pins.map(pin => pin.identity)); + } + + private async getAllLocalDependencySet( + workspaceState: WorkspaceState, + folderContext: FolderContext + ): Promise> { + const rootDependencies = folderContext?.swiftPackage.dependencies ?? []; + const showingDependencies = new Set(); + await this.getChildLocalDependencySet( + rootDependencies, + workspaceState?.object.dependencies ?? [], + folderContext?.folder.fsPath, + showingDependencies + ); + + return showingDependencies; + } + + /** + * tranverse only local dependency tree + * @param dependencies depdencies of current node + * @param workspaceStateDependencies all dependencies in workspace-state.json + * @param showingDependencies result of dependencies that are showing in the tree + * @returns + */ + private async getChildLocalDependencySet( + dependencies: Dependency[], + workspaceStateDependencies: WorkspaceStateDependency[], + workspacePath: string, + showingDependencies: Set + ) { + for (let i = 0; i < dependencies.length; i++) { + const childDependency = dependencies[i]; + if (showingDependencies.has(childDependency.identity)) { + continue; + } + + if (childDependency.type !== "local" && childDependency.type !== "fileSystem") { + continue; + } + + showingDependencies.add(childDependency.identity); + const workspaceStateDependency = workspaceStateDependencies.find( + workspaceStateDependency => + workspaceStateDependency.packageRef.identity === childDependency.identity + ); + if (!workspaceStateDependency) { + continue; + } + + const packagePath = this.dependencyPackagePath(workspaceStateDependency, workspacePath); + + const childDependencyContents = (await SwiftPackage.loadPackage( + vscode.Uri.file(packagePath) + )) as PackageContents; + + await this.getChildLocalDependencySet( + childDependencyContents.dependencies, + workspaceStateDependencies, + workspacePath, + showingDependencies + ); + } } /** From 97a0fe35c877b7b6915b06f69de3121f95708ca7 Mon Sep 17 00:00:00 2001 From: Wang Lun Date: Wed, 10 Aug 2022 18:02:00 +0900 Subject: [PATCH 6/7] added comments for dependencies functions, simplified code --- src/ui/PackageDependencyProvider.ts | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/ui/PackageDependencyProvider.ts b/src/ui/PackageDependencyProvider.ts index f47f98d88..f2ecc4b6f 100644 --- a/src/ui/PackageDependencyProvider.ts +++ b/src/ui/PackageDependencyProvider.ts @@ -193,6 +193,13 @@ export class PackageDependenciesProvider implements vscode.TreeDataProvider(); - localDependencies.forEach(dependency => inUseDependencies.add(dependency)); - remoteDependencies.forEach(dependency => inUseDependencies.add(dependency)); - return inUseDependencies; + return new Set([...localDependencies, ...remoteDependencies]); } private getRemoteDependencySet(folderContext: FolderContext | undefined): Set { return new Set(folderContext?.swiftPackage.resolved?.pins.map(pin => pin.identity)); } + /** + * convert `getChildLocalDependencySet` to Promise> + * @param workspaceState the workspace state read from `Workspace-state.json` + * @param folderContext the folder context of the current folder + * @returns + */ private async getAllLocalDependencySet( workspaceState: WorkspaceState, folderContext: FolderContext From 34151a3846830e8f35cbb5438a3fa74fdeb55b9b Mon Sep 17 00:00:00 2001 From: Wang Lun Date: Thu, 11 Aug 2022 00:09:24 +0900 Subject: [PATCH 7/7] change recursive to stack, add getEdited --- src/ui/PackageDependencyProvider.ts | 80 +++++++++++++---------------- 1 file changed, 35 insertions(+), 45 deletions(-) diff --git a/src/ui/PackageDependencyProvider.ts b/src/ui/PackageDependencyProvider.ts index f2ecc4b6f..c108c2acb 100644 --- a/src/ui/PackageDependencyProvider.ts +++ b/src/ui/PackageDependencyProvider.ts @@ -196,7 +196,7 @@ export class PackageDependenciesProvider implements vscode.TreeDataProvider> { - const localDependencies = await this.getAllLocalDependencySet( - workspaceState, - folderContext - ); + const localDependencies = await this.getLocalDependencySet(workspaceState, folderContext); const remoteDependencies = this.getRemoteDependencySet(folderContext); - return new Set([...localDependencies, ...remoteDependencies]); + const editedDependencies = this.getEditedDependencySet(workspaceState); + return new Set([ + ...localDependencies, + ...remoteDependencies, + ...editedDependencies, + ]); } private getRemoteDependencySet(folderContext: FolderContext | undefined): Set { return new Set(folderContext?.swiftPackage.resolved?.pins.map(pin => pin.identity)); } + private getEditedDependencySet(workspaceState: WorkspaceState): Set { + return new Set( + workspaceState.object.dependencies + .filter(dependency => this.dependencyType(dependency) === "editing") + .map(dependency => dependency.packageRef.identity) + ); + } + /** - * convert `getChildLocalDependencySet` to Promise> * @param workspaceState the workspace state read from `Workspace-state.json` * @param folderContext the folder context of the current folder - * @returns + * @returns all local in-use dependencies */ - private async getAllLocalDependencySet( + private async getLocalDependencySet( workspaceState: WorkspaceState, folderContext: FolderContext ): Promise> { - const rootDependencies = folderContext?.swiftPackage.dependencies ?? []; - const showingDependencies = new Set(); - await this.getChildLocalDependencySet( - rootDependencies, - workspaceState?.object.dependencies ?? [], - folderContext?.folder.fsPath, - showingDependencies - ); + const rootDependencies = folderContext.swiftPackage.dependencies ?? []; + const workspaceStateDependencies = workspaceState.object.dependencies ?? []; + const workspacePath = folderContext.folder.fsPath; - return showingDependencies; - } + const showingDependencies: Set = new Set(); + const stack: Dependency[] = rootDependencies; - /** - * tranverse only local dependency tree - * @param dependencies depdencies of current node - * @param workspaceStateDependencies all dependencies in workspace-state.json - * @param showingDependencies result of dependencies that are showing in the tree - * @returns - */ - private async getChildLocalDependencySet( - dependencies: Dependency[], - workspaceStateDependencies: WorkspaceStateDependency[], - workspacePath: string, - showingDependencies: Set - ) { - for (let i = 0; i < dependencies.length; i++) { - const childDependency = dependencies[i]; - if (showingDependencies.has(childDependency.identity)) { + while (stack.length > 0) { + const top = stack.pop(); + if (!top) { + continue; + } + + if (showingDependencies.has(top.identity)) { continue; } - if (childDependency.type !== "local" && childDependency.type !== "fileSystem") { + if (top.type !== "local" && top.type !== "fileSystem") { continue; } - showingDependencies.add(childDependency.identity); + showingDependencies.add(top.identity); const workspaceStateDependency = workspaceStateDependencies.find( workspaceStateDependency => - workspaceStateDependency.packageRef.identity === childDependency.identity + workspaceStateDependency.packageRef.identity === top.identity ); if (!workspaceStateDependency) { continue; } const packagePath = this.dependencyPackagePath(workspaceStateDependency, workspacePath); - const childDependencyContents = (await SwiftPackage.loadPackage( vscode.Uri.file(packagePath) )) as PackageContents; - await this.getChildLocalDependencySet( - childDependencyContents.dependencies, - workspaceStateDependencies, - workspacePath, - showingDependencies - ); + stack.push(...childDependencyContents.dependencies); } + return showingDependencies; } /**