Skip to content

load packages from workspace-state.json, missing packages can show #370

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
wants to merge 17 commits into from
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
9 changes: 7 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -296,13 +296,17 @@
"command": "swift.useLocalDependency",
"when": "view == packageDependencies && viewItem == remote"
},
{
"command": "swift.editDependency",
"when": "view == packageDependencies && viewItem == remote"
},
{
"command": "swift.uneditDependency",
"when": "view == packageDependencies && viewItem == editing"
"when": "view == packageDependencies && viewItem == edited"
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Do you mind this change

},
{
"command": "swift.openInWorkspace",
"when": "view == packageDependencies && viewItem == editing"
"when": "view == packageDependencies && viewItem == edited"
},
{
"command": "swift.openInWorkspace",
Expand Down Expand Up @@ -434,3 +438,4 @@
"vscode-languageclient": "^8.0.0"
}
}

12 changes: 10 additions & 2 deletions src/FolderContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import * as vscode from "vscode";
import * as path from "path";
import { LinuxMain } from "./LinuxMain";
import { PackageWatcher } from "./PackageWatcher";
import { SwiftPackage } from "./SwiftPackage";
import { SwiftPackage, WorkspaceStateDependency } from "./SwiftPackage";
import { TestExplorer } from "./TestExplorer/TestExplorer";
import { WorkspaceContext, FolderEvent } from "./WorkspaceContext";
import { BackgroundCompilation } from "./BackgroundCompilation";
Expand Down Expand Up @@ -150,11 +150,19 @@ export class FolderContext implements vscode.Disposable {
return item.state.name === "edited" && item.state.path;
})
.map(item => {
return { name: item.packageRef.identity, folder: item.state.path! };
return {
name: item.packageRef.identity,
folder: item.state.path!,
};
}) ?? []
);
}

/** Get list in-use packages */
async resolveDependencyGraph(): Promise<WorkspaceStateDependency[]> {
return await this.swiftPackage.resolveDependencyGraph();
}

static uriName(uri: vscode.Uri): string {
return path.basename(uri.fsPath);
}
Expand Down
27 changes: 27 additions & 0 deletions src/PackageWatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import * as vscode from "vscode";
import { FolderContext } from "./FolderContext";
import { buildDirectoryFromWorkspacePath } from "./utilities/utilities";
import { FolderEvent, WorkspaceContext } from "./WorkspaceContext";

/**
Expand All @@ -25,6 +26,7 @@ import { FolderEvent, WorkspaceContext } from "./WorkspaceContext";
export class PackageWatcher {
private packageFileWatcher?: vscode.FileSystemWatcher;
private resolvedFileWatcher?: vscode.FileSystemWatcher;
private workspaceStateFileWatcher?: vscode.FileSystemWatcher;

constructor(private folderContext: FolderContext, private workspaceContext: WorkspaceContext) {}

Expand All @@ -35,6 +37,7 @@ export class PackageWatcher {
install() {
this.packageFileWatcher = this.createPackageFileWatcher();
this.resolvedFileWatcher = this.createResolvedFileWatcher();
this.workspaceStateFileWatcher = this.createWorkspaceStateFileWatcher();
}

/**
Expand All @@ -44,6 +47,7 @@ export class PackageWatcher {
dispose() {
this.packageFileWatcher?.dispose();
this.resolvedFileWatcher?.dispose();
this.workspaceStateFileWatcher?.dispose();
}

private createPackageFileWatcher(): vscode.FileSystemWatcher {
Expand All @@ -66,6 +70,20 @@ export class PackageWatcher {
return watcher;
}

private createWorkspaceStateFileWatcher(): vscode.FileSystemWatcher {
const uri = vscode.Uri.file(
buildDirectoryFromWorkspacePath(this.folderContext.folder.fsPath, true)
);

const watcher = vscode.workspace.createFileSystemWatcher(
new vscode.RelativePattern(uri, "workspace-state.json")
);
watcher.onDidCreate(async () => await this.handleWorkspaceStateChange());
watcher.onDidChange(async () => await this.handleWorkspaceStateChange());
watcher.onDidDelete(async () => await this.handleWorkspaceStateChange());
return watcher;
}

/**
* Handles a create or change event for **Package.swift**.
*
Expand All @@ -88,4 +106,13 @@ export class PackageWatcher {
await this.folderContext.reloadPackageResolved();
this.workspaceContext.fireEvent(this.folderContext, FolderEvent.resolvedUpdated);
}

/**
* Handles a create or change event for **workspace-state.json**.
*
* This will resolve any changes in the workspace-state.json
*/
private async handleWorkspaceStateChange() {
this.workspaceContext.fireEvent(this.folderContext, FolderEvent.workspaceStateUpdated);
}
}
133 changes: 132 additions & 1 deletion src/SwiftPackage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import * as vscode from "vscode";
import * as fs from "fs/promises";
import * as path from "path";
import {
buildDirectoryFromWorkspacePath,
execSwift,
Expand Down Expand Up @@ -117,9 +118,17 @@ export interface WorkspaceState {
version: number;
}

/** branch + revision || revision + version */
export interface CheckoutState {
branch: string | null;
revision: string;
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 All @@ -143,6 +152,61 @@ function isError(state: SwiftPackageState): state is Error {
return state instanceof Error;
}

/**
* Get version of WorkspaceStateDependency for displaying in the tree
* @param dependency
* @return real version | edited | local
*/
export function dependencyVersion(dependency: WorkspaceStateDependency): string {
return dependency.packageRef.kind === "fileSystem"
? "local"
: dependency.state.checkoutState?.version ??
dependency.state.checkoutState?.branch ??
"edited";
}

/**
* Get type of WorkspaceStateDependency for displaying in the tree: real version | edited | local
* @param dependency
* @return "local" | "remote" | "edited"
*/
export function dependencyType(
dependency: WorkspaceStateDependency
): "local" | "remote" | "edited" {
return dependency.state.name === "edited"
? "edited"
: dependency.packageRef.kind === "fileSystem"
? "local"
: "remote";
}

/**
* Get type of WorkspaceStateDependency for displaying in the tree: real version | edited | local
* `edited`: dependency.state.path ?? workspacePath + Packages/ + dependency.subpath
* `local`: dependency.packageRef.location
* `remote`: buildDirectory + checkouts + dependency.packageRef.location
* @param dependency
* @return the package path based on the type
*/
export function dependencyPackagePath(
dependency: WorkspaceStateDependency,
workspaceFolder: string
): string {
const type = dependencyType(dependency);
let packagePath = "";
if (type === "edited") {
packagePath =
dependency.state.path ?? path.join(workspaceFolder, "Packages", dependency.subpath);
} else if (type === "local") {
packagePath = dependency.state.path ?? dependency.packageRef.location;
} else {
// remote
const buildDirectory = buildDirectoryFromWorkspacePath(workspaceFolder, true);
packagePath = path.join(buildDirectory, "checkouts", dependency.subpath);
}
return packagePath;
}

/**
* Class holding Swift Package Manager Package
*/
Expand Down Expand Up @@ -267,6 +331,73 @@ export class SwiftPackage implements PackageContents {
}
}

/**
* tranverse the dependency tree
* in each node, call `swift package describe` to get the child dependencies and do it recursively
* @returns all dependencies in flat list
*/
async resolveDependencyGraph(): Promise<WorkspaceStateDependency[]> {
const workspaceState = await this.loadWorkspaceState();
const workspaceStateDependencies = workspaceState?.object.dependencies ?? [];

if (workspaceStateDependencies.length === 0) {
return [];
}

const contents = this.contents as PackageContents;
const showingDependencies = new Set<string>();
await this.getChildDependencies(contents, workspaceStateDependencies, showingDependencies);

// filter workspaceStateDependencies that in showingDependencies
return workspaceStateDependencies.filter(dependency =>
showingDependencies.has(dependency.packageRef.identity)
);
}

/**
* tranverse the dependency tree
* @param dependency current node
* @param workspaceStateDependencies all dependencies in workspace-state.json
* @param showingDependencies result of dependencies that are showing in the tree
* @returns
*/
private async getChildDependencies(
dependency: PackageContents,
workspaceStateDependencies: WorkspaceStateDependency[],
showingDependencies: Set<string>
) {
for (let i = 0; i < dependency.dependencies.length; i++) {
const childDependency = dependency.dependencies[i];
if (showingDependencies.has(childDependency.identity)) {
return;
}
showingDependencies.add(childDependency.identity);
const workspaceStateDependency = workspaceStateDependencies.find(
workspaceStateDependency =>
workspaceStateDependency.packageRef.identity === childDependency.identity
);
if (workspaceStateDependency) {
showingDependencies.add(workspaceStateDependency.packageRef.identity);
}

if (workspaceStateDependency === undefined) {
return;
}

const packagePath = dependencyPackagePath(workspaceStateDependency, this.folder.fsPath);

const childDependencyContents = (await SwiftPackage.loadPackage(
vscode.Uri.file(packagePath)
)) as PackageContents;

await this.getChildDependencies(
childDependencyContents,
workspaceStateDependencies,
showingDependencies
);
}
}

/** Reload swift package */
public async reload() {
this.contents = await SwiftPackage.loadPackage(this.folder);
Expand Down
5 changes: 5 additions & 0 deletions src/WorkspaceContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,12 @@ export class WorkspaceContext implements vscode.Disposable {
this.updateContextKeys(folder);
break;
case FolderEvent.resolvedUpdated:
break;
case FolderEvent.workspaceStateUpdated:
if (folder === this.currentFolder) {
this.updateContextKeys(folder);
}
break;
}
});
this.subscriptions = [
Expand Down Expand Up @@ -491,6 +494,8 @@ export enum FolderEvent {
packageUpdated = "packageUpdated",
// Package.resolved has been updated
resolvedUpdated = "resolvedUpdated",
// `workspace-state.json` is updated, update dependency tree only by this event
workspaceStateUpdated = "workspaceStateUpdated",
}

/** Workspace Folder observer function */
Expand Down
11 changes: 6 additions & 5 deletions src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -432,11 +432,12 @@ function updateAfterError(result: boolean, folderContext: FolderContext) {
const triggerResolvedUpdatedEvent = folderContext.hasResolveErrors;
// set has resolve errors flag
folderContext.hasResolveErrors = !result;

// if previous folder state was with resolve errors, and now it is without then
// send Package.resolved updated event to trigger display of package dependencies
// send workspace-state.json updated event to trigger display of package dependencies
// view
if (triggerResolvedUpdatedEvent && !folderContext.hasResolveErrors) {
folderContext.fireEvent(FolderEvent.resolvedUpdated);
folderContext.fireEvent(FolderEvent.workspaceStateUpdated);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

only trigger UI updates

}
}

Expand All @@ -455,17 +456,17 @@ export function register(ctx: WorkspaceContext) {
vscode.commands.registerCommand("swift.openPackage", () => openPackage(ctx)),
vscode.commands.registerCommand("swift.useLocalDependency", item => {
if (item instanceof PackageNode) {
useLocalDependency(item.name, ctx);
useLocalDependency(item.identity, ctx);
}
}),
vscode.commands.registerCommand("swift.editDependency", item => {
if (item instanceof PackageNode) {
editDependency(item.name, ctx);
editDependency(item.identity, ctx);
}
}),
vscode.commands.registerCommand("swift.uneditDependency", item => {
if (item instanceof PackageNode) {
uneditDependency(item.name, ctx);
uneditDependency(item.identity, ctx);
}
}),
vscode.commands.registerCommand("swift.openInWorkspace", item => {
Expand Down
Loading