Skip to content

load dependency graph based on data of workspace-state.json #378

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@
},
{
"command": "swift.openExternal",
"when": "view == packageDependencies && viewItem == remote"
"when": "view == packageDependencies && viewItem != local"
}
]
},
Expand Down
12 changes: 11 additions & 1 deletion src/SwiftPackage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
130 changes: 98 additions & 32 deletions src/ui/PackageDependencyProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ 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";
import contextKeys from "../contextKeys";
import { WorkspaceState } from "../SwiftPackage";
import { WorkspaceState, WorkspaceStateDependency } from "../SwiftPackage";

/**
* References:
Expand All @@ -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"
) {}
Expand Down Expand Up @@ -148,39 +149,31 @@ export class PackageDependenciesProvider implements vscode.TreeDataProvider<Tree
}
if (!element) {
const workspaceState = await folderContext.swiftPackage.loadWorkspaceState();
// Build PackageNodes for all dependencies. Because Package.resolved might not
// be up to date with edited dependency list, we need to remove the edited
// dependencies from the list before adding in the edit version
const children = [
...this.getLocalDependencies(workspaceState),
...this.getRemoteDependencies(folderContext),
];
const editedChildren = await this.getEditedDependencies(workspaceState);
const uneditedChildren: PackageNode[] = [];
for (const child of children) {
const editedVersion = editedChildren.find(item => 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);
const location = dependency.packageRef.location;
return new PackageNode(
dependency.packageRef.identity,
packagePath,
location,
version,
type
);
}) ?? []
);
}

/**
Expand All @@ -204,6 +197,7 @@ export class PackageDependenciesProvider implements vscode.TreeDataProvider<Tree
new PackageNode(
dependency.packageRef.identity,
dependency.packageRef.location,
dependency.packageRef.location,
"local",
"local"
)
Expand All @@ -221,6 +215,7 @@ export class PackageDependenciesProvider implements vscode.TreeDataProvider<Tree
new PackageNode(
pin.identity,
pin.location,
pin.location,
pin.state.version ?? pin.state.branch ?? pin.state.revision.substring(0, 7),
"remote"
)
Expand All @@ -244,6 +239,7 @@ export class PackageDependenciesProvider implements vscode.TreeDataProvider<Tree
new PackageNode(
item.packageRef.identity,
item.state.path!,
item.state.path!,
"local",
"editing"
)
Expand Down Expand Up @@ -277,4 +273,74 @@ export class PackageDependenciesProvider implements vscode.TreeDataProvider<Tree
}
});
}

/// - Dependency display helpers

/**
* Get type of WorkspaceStateDependency for displaying in the tree: real version | edited | local
* @param dependency
* @return "local" | "remote" | "editing"
*/
private dependencyType(dependency: WorkspaceStateDependency): "local" | "remote" | "editing" {
if (dependency.state.name === "edited") {
return "editing";
} else if (
dependency.packageRef.kind === "local" ||
dependency.packageRef.kind === "fileSystem"
) {
// need to check for both "local" and "fileSystem" as swift 5.5 and earlier
// use "local" while 5.6 and later use "fileSystem"
return "local";
} else {
return "remote";
}
}

/**
* Get version of WorkspaceStateDependency for displaying in the tree
* @param dependency
* @return real version | editing | local
*/
private dependencyDisplayVersion(dependency: WorkspaceStateDependency): string {
const type = this.dependencyType(dependency);
if (type === "editing") {
return "editing";
} else if (type === "local") {
return "local";
} else {
return (
dependency.state.checkoutState?.version ??
dependency.state.checkoutState?.branch ??
dependency.state.checkoutState?.revision.substring(0, 7) ??
"unknown"
);
}
}

/**
* * Get package source path of dependency
* `editing`: dependency.state.path ?? workspacePath + Packages/ + dependency.subpath
* `local`: dependency.packageRef.location
* `remote`: buildDirectory + checkouts + dependency.packageRef.location
* @param dependency
* @param workspaceFolder
* @return the package path based on the type
*/
private dependencyPackagePath(
dependency: WorkspaceStateDependency,
workspaceFolder: string
): string {
const type = this.dependencyType(dependency);
if (type === "editing") {
return (
dependency.state.path ?? path.join(workspaceFolder, "Packages", dependency.subpath)
);
} else if (type === "local") {
return dependency.state.path ?? dependency.packageRef.location;
} else {
// remote
const buildDirectory = buildDirectoryFromWorkspacePath(workspaceFolder, true);
return path.join(buildDirectory, "checkouts", dependency.subpath);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Package path is used for different things, based on the kind of dependency you have. Remote dependencies store the web address of the repo in there so the View Repository item in the right click menu works. You can either resurrect this setup or add a location parameter to PackageNode which stores the repo URL (Your call). If you add the location parameter you will need to change the code for openInExternalEditor in commands.ts to use this parameter.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!
let me add a location

Do we show View Repository for editing packages?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not at the moment, but if we have a separate parameter we could.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've fixed it in 8d7b216

}
}
}