Skip to content
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

Show error message in project panel when manifest can't be built #1453

Merged
merged 1 commit into from
Mar 21, 2025
Merged
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
6 changes: 6 additions & 0 deletions src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
//
//===----------------------------------------------------------------------===//

import * as path from "path";
import * as vscode from "vscode";
import { WorkspaceContext } from "./WorkspaceContext";
import { PackageNode } from "./ui/ProjectPanelProvider";
Expand Down Expand Up @@ -93,6 +94,7 @@ export enum Commands {
RUN_ALL_TESTS_PARALLEL = "swift.runAllTestsParallel",
DEBUG_ALL_TESTS = "swift.debugAllTests",
COVER_ALL_TESTS = "swift.coverAllTests",
OPEN_MANIFEST = "swift.openManifest",
}

/**
Expand Down Expand Up @@ -209,6 +211,10 @@ export function register(ctx: WorkspaceContext): vscode.Disposable[] {
vscode.commands.registerCommand("swift.openEducationalNote", uri =>
openEducationalNote(uri)
),
vscode.commands.registerCommand(Commands.OPEN_MANIFEST, (uri: vscode.Uri) => {
const packagePath = path.join(uri.fsPath, "Package.swift");
vscode.commands.executeCommand("vscode.open", vscode.Uri.file(packagePath));
}),
];
}

Expand Down
57 changes: 54 additions & 3 deletions src/ui/ProjectPanelProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -311,12 +311,44 @@ class HeaderNode {
}
}

class ErrorNode {
constructor(
public name: string,
private folder: vscode.Uri
) {}

get path(): string {
return "";
}

toTreeItem(): vscode.TreeItem {
const item = new vscode.TreeItem(this.name, vscode.TreeItemCollapsibleState.None);
item.id = `error-${this.folder.fsPath}`;
item.iconPath = new vscode.ThemeIcon("error", new vscode.ThemeColor("errorForeground"));
item.contextValue = "error";
item.accessibilityInformation = { label: this.name };
item.tooltip =
"Could not build the Package.swift, fix the error to refresh the project panel";

item.command = {
command: "swift.openManifest",
arguments: [this.folder],
title: "Open Manifest",
};
return item;
}

getChildren(): Promise<TreeNode[]> {
return Promise.resolve([]);
}
}

/**
* A node in the Package Dependencies {@link vscode.TreeView TreeView}.
*
* Can be either a {@link PackageNode}, {@link FileNode}, {@link TargetNode}, {@link TaskNode} or {@link HeaderNode}.
* Can be either a {@link PackageNode}, {@link FileNode}, {@link TargetNode}, {@link TaskNode}, {@link ErrorNode} or {@link HeaderNode}.
*/
type TreeNode = PackageNode | FileNode | HeaderNode | TaskNode | TargetNode;
type TreeNode = PackageNode | FileNode | HeaderNode | TaskNode | TargetNode | ErrorNode;

/**
* A {@link vscode.TreeDataProvider<T> TreeDataProvider} for project dependencies, tasks and commands {@link vscode.TreeView TreeView}.
Expand All @@ -328,6 +360,7 @@ export class ProjectPanelProvider implements vscode.TreeDataProvider<TreeNode> {
private workspaceObserver?: vscode.Disposable;
private disposables: vscode.Disposable[] = [];
private activeTasks: Set<string> = new Set();
private lastComputedNodes: TreeNode[] = [];

onDidChangeTreeData = this.didChangeTreeDataEmitter.event;

Expand Down Expand Up @@ -426,6 +459,24 @@ export class ProjectPanelProvider implements vscode.TreeDataProvider<TreeNode> {
return [];
}

if (!element && folderContext.hasResolveErrors) {
return [
new ErrorNode("Error Parsing Package.swift", folderContext.folder),
...this.lastComputedNodes,
];
}

const nodes = await this.computeChildren(folderContext, element);

// If we're fetching the root nodes then save them in case we have an error later,
// in which case we show the ErrorNode along with the last known good nodes.
if (!element) {
this.lastComputedNodes = nodes;
}
return nodes;
}

async computeChildren(folderContext: FolderContext, element?: TreeNode): Promise<TreeNode[]> {
if (element) {
return element.getChildren();
}
Expand Down Expand Up @@ -515,7 +566,7 @@ export class ProjectPanelProvider implements vscode.TreeDataProvider<TreeNode> {
);
}

private async tasks(folderContext: FolderContext): Promise<TreeNode[]> {
private async tasks(folderContext: FolderContext): Promise<TaskNode[]> {
const tasks = await vscode.tasks.fetchTasks();

return (
Expand Down
9 changes: 9 additions & 0 deletions test/integration-tests/ui/ProjectPanelProvider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,15 @@ suite("ProjectPanelProvider Test Suite", function () {
expect(items.find(n => n.name === "swift-markdown")).to.not.be.undefined;
expect(items.find(n => n.name === "defaultpackage")).to.not.be.undefined;
});

test("Shows an error node when there is a problem compiling Package.swift", async () => {
workspaceContext.folders[0].hasResolveErrors = true;
workspaceContext.currentFolder = workspaceContext.folders[0];
const treeProvider = new ProjectPanelProvider(workspaceContext);
const children = await treeProvider.getChildren();
const errorNode = children.find(n => n.name === "Error Parsing Package.swift");
expect(errorNode).to.not.be.undefined;
});
});

async function getHeaderChildren(headerName: string) {
Expand Down
Loading