Skip to content

Commit 674cae7

Browse files
authored
Show error message in project panel when manifest can't be built (#1453)
Because we use `swift package describe` to extract metadata about the package we need the Package.swift to be buildable. If it isn't, create a header node in the project panel that calls out the fact that the Package.swift can't be built. Clicking it will take the user to the Package.swift.
1 parent 64de5b7 commit 674cae7

File tree

3 files changed

+69
-3
lines changed

3 files changed

+69
-3
lines changed

src/commands.ts

+6
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
//
1313
//===----------------------------------------------------------------------===//
1414

15+
import * as path from "path";
1516
import * as vscode from "vscode";
1617
import { WorkspaceContext } from "./WorkspaceContext";
1718
import { PackageNode } from "./ui/ProjectPanelProvider";
@@ -93,6 +94,7 @@ export enum Commands {
9394
RUN_ALL_TESTS_PARALLEL = "swift.runAllTestsParallel",
9495
DEBUG_ALL_TESTS = "swift.debugAllTests",
9596
COVER_ALL_TESTS = "swift.coverAllTests",
97+
OPEN_MANIFEST = "swift.openManifest",
9698
}
9799

98100
/**
@@ -209,6 +211,10 @@ export function register(ctx: WorkspaceContext): vscode.Disposable[] {
209211
vscode.commands.registerCommand("swift.openEducationalNote", uri =>
210212
openEducationalNote(uri)
211213
),
214+
vscode.commands.registerCommand(Commands.OPEN_MANIFEST, (uri: vscode.Uri) => {
215+
const packagePath = path.join(uri.fsPath, "Package.swift");
216+
vscode.commands.executeCommand("vscode.open", vscode.Uri.file(packagePath));
217+
}),
212218
];
213219
}
214220

src/ui/ProjectPanelProvider.ts

+54-3
Original file line numberDiff line numberDiff line change
@@ -311,12 +311,44 @@ class HeaderNode {
311311
}
312312
}
313313

314+
class ErrorNode {
315+
constructor(
316+
public name: string,
317+
private folder: vscode.Uri
318+
) {}
319+
320+
get path(): string {
321+
return "";
322+
}
323+
324+
toTreeItem(): vscode.TreeItem {
325+
const item = new vscode.TreeItem(this.name, vscode.TreeItemCollapsibleState.None);
326+
item.id = `error-${this.folder.fsPath}`;
327+
item.iconPath = new vscode.ThemeIcon("error", new vscode.ThemeColor("errorForeground"));
328+
item.contextValue = "error";
329+
item.accessibilityInformation = { label: this.name };
330+
item.tooltip =
331+
"Could not build the Package.swift, fix the error to refresh the project panel";
332+
333+
item.command = {
334+
command: "swift.openManifest",
335+
arguments: [this.folder],
336+
title: "Open Manifest",
337+
};
338+
return item;
339+
}
340+
341+
getChildren(): Promise<TreeNode[]> {
342+
return Promise.resolve([]);
343+
}
344+
}
345+
314346
/**
315347
* A node in the Package Dependencies {@link vscode.TreeView TreeView}.
316348
*
317-
* Can be either a {@link PackageNode}, {@link FileNode}, {@link TargetNode}, {@link TaskNode} or {@link HeaderNode}.
349+
* Can be either a {@link PackageNode}, {@link FileNode}, {@link TargetNode}, {@link TaskNode}, {@link ErrorNode} or {@link HeaderNode}.
318350
*/
319-
type TreeNode = PackageNode | FileNode | HeaderNode | TaskNode | TargetNode;
351+
type TreeNode = PackageNode | FileNode | HeaderNode | TaskNode | TargetNode | ErrorNode;
320352

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

332365
onDidChangeTreeData = this.didChangeTreeDataEmitter.event;
333366

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

462+
if (!element && folderContext.hasResolveErrors) {
463+
return [
464+
new ErrorNode("Error Parsing Package.swift", folderContext.folder),
465+
...this.lastComputedNodes,
466+
];
467+
}
468+
469+
const nodes = await this.computeChildren(folderContext, element);
470+
471+
// If we're fetching the root nodes then save them in case we have an error later,
472+
// in which case we show the ErrorNode along with the last known good nodes.
473+
if (!element) {
474+
this.lastComputedNodes = nodes;
475+
}
476+
return nodes;
477+
}
478+
479+
async computeChildren(folderContext: FolderContext, element?: TreeNode): Promise<TreeNode[]> {
429480
if (element) {
430481
return element.getChildren();
431482
}
@@ -515,7 +566,7 @@ export class ProjectPanelProvider implements vscode.TreeDataProvider<TreeNode> {
515566
);
516567
}
517568

518-
private async tasks(folderContext: FolderContext): Promise<TreeNode[]> {
569+
private async tasks(folderContext: FolderContext): Promise<TaskNode[]> {
519570
const tasks = await vscode.tasks.fetchTasks();
520571

521572
return (

test/integration-tests/ui/ProjectPanelProvider.test.ts

+9
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,15 @@ suite("ProjectPanelProvider Test Suite", function () {
282282
expect(items.find(n => n.name === "swift-markdown")).to.not.be.undefined;
283283
expect(items.find(n => n.name === "defaultpackage")).to.not.be.undefined;
284284
});
285+
286+
test("Shows an error node when there is a problem compiling Package.swift", async () => {
287+
workspaceContext.folders[0].hasResolveErrors = true;
288+
workspaceContext.currentFolder = workspaceContext.folders[0];
289+
const treeProvider = new ProjectPanelProvider(workspaceContext);
290+
const children = await treeProvider.getChildren();
291+
const errorNode = children.find(n => n.name === "Error Parsing Package.swift");
292+
expect(errorNode).to.not.be.undefined;
293+
});
285294
});
286295

287296
async function getHeaderChildren(headerName: string) {

0 commit comments

Comments
 (0)