fix(project-tree): sort workspace projects by folder name (#1583)

Workspace member roots were listed in manifest order. The Project and Project Files views now sort them by the displayed folder name with the same natural, case-insensitive comparison as tree entries; the stored member order is unchanged.

Co-authored-by: shohruh-genius <[email protected]>
This commit is contained in:
Shohruh Ismoilovandshohruh-genius authored and GitHub committed 2026-11-06 00:17:37 +08:00
1 parent 31e477b7fb
commit af0ffb809e
4 files changed
+31 -14

No files matched your search

+18 -4
View File
@@ -664,10 +664,24 @@ describe("Show Members", () => {
});
});
it("renders workspace member roots with their own paths, without the settings directory", async () => {
fs = new MemoryFs({ "/z/first.txt": "", "/a/second.txt": "", "/workspace/.openidea/workspace.json": "{}" });
it("sorts workspace member roots by the folder names shown in the tree", async () => {
fs = new MemoryFs({
"/a/service10/ten.txt": "",
"/z/Service2/two.txt": "",
"/m/alpha/first.txt": "",
"/workspace/.openidea/workspace.json": "{}",
});
setProjectFsForTests(fs, "/workspace");
initProjectTree("/workspace", "Workspace", ["/z", "/a"]);
initProjectTree("/workspace", "Workspace", ["/a/service10", "/z/Service2", "/m/alpha"]);
const { rows } = setup();
await waitFor(() => expect(rows()).toEqual(["z/z", "first.txt", "a/a", "second.txt"]));
await waitFor(() =>
expect(rows()).toEqual([
"alpha/m/alpha",
"first.txt",
"Service2/z/Service2",
"two.txt",
"service10/a/service10",
"ten.txt",
]),
);
});
+5 -1
View File
@@ -100,6 +100,10 @@ export function isVisible(entry: FileEntry, opts: TreeOptions): boolean {
const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: "base" });
export function compareNames(a: string, b: string): number {
return collator.compare(a, b) || (a < b ? -1 : a > b ? 1 : 0);
}
/** Symlinks report the target's metadata; folders have size 0 (the entry has no target kind). */
export const isDirEntry = (e: FileEntry) => e.kind === "dir" || (e.kind === "symlink" && e.size === 0);
@@ -125,7 +129,7 @@ export function sortEntries(
const m = (b.mtimeMs ?? 0) - (a.mtimeMs ?? 0);
if (m) return m;
}
return collator.compare(a.name, b.name) || (a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
return compareNames(a.name, b.name);
});
}
+3 -2
View File
@@ -104,12 +104,13 @@ describe("creating packages", () => {
});
});
it("uses only manifest members in order in workspace Project and Project Files views", () => {
it("shows manifest members in name order in workspace Project and Project Files views", () => {
for (const mode of ["project", "projectFiles"] as const) {
initProjectTree("/workspace", "Workspace", ["/z", "/a"]);
useProjectTree.setState({ mode });
const model = selectModel(useProjectTree.getState());
expect(model.roots.map((r) => r.path)).toEqual(["/z", "/a"]);
expect(model.roots.map((r) => r.path)).toEqual(["/a", "/z"]);
expect(useProjectTree.getState().members).toEqual(["/z", "/a"]);
expect(model.byKey.has("/workspace")).toBe(false);
initProjectTree("/workspace", "Workspace", ["/a"]);
expect(selectModel(useProjectTree.getState()).roots.map((r) => r.path)).toEqual(["/a"]);
+5 -7
View File
@@ -14,6 +14,7 @@ import {
GROUP_PREFIX,
buildModel,
buildPathsModel,
compareNames,
extraFolders,
isGroupKey,
isLibKey,
@@ -34,7 +35,7 @@ import {
} from "./model";
import { projectFs } from "./fs";
import { projectViewRoots, useProjectViewRoots, type ProjectViewRootContribution } from "@/workbench/contributions";
import { ancestorsWithin, isSameOrInside, type PackageKind } from "./paths";
import { ancestorsWithin, isSameOrInside, nameOf, type PackageKind } from "./paths";
export interface ViewOptions extends TreeOptions {
/** Select the active editor's file in the tree. */
@@ -218,12 +219,9 @@ export function selectModel(s: ProjectTreeState): TreeModel {
const extras = useProjectViewRoots.getState().roots;
const options = treeOptions(s);
const contributed = projectViewRoots(extras);
const memberRoots = s.members?.map((path) => ({
path,
label: path.slice(path.lastIndexOf("/") + 1),
icon: "",
detail: path,
}));
const memberRoots = s.members
?.map((path) => ({ path, label: nameOf(path), icon: "", detail: path }))
.sort((a, b) => compareNames(a.label, b.label));
const project = () =>
memberRoots
? withExtraRoots(EMPTY_MODEL, memberRoots, s.listings, options, placeholder)