From 98cc2b434fc6d35824009ade2ceffe78bf8d76fe Mon Sep 17 00:00:00 2001 From: luke2x <97411838+luke2x@users.noreply.github.com> Date: Sun, 18 Oct 2026 20:51:38 +0100 Subject: [PATCH 1/3] perf(mobile): flatten file trees without copying descendant arrays --- apps/mobile/src/features/files/fileTree.test.ts | 14 ++++++++++++++ apps/mobile/src/features/files/fileTree.ts | 8 ++++---- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/apps/mobile/src/features/files/fileTree.test.ts b/apps/mobile/src/features/files/fileTree.test.ts index eb97254b8a307..ec09414204ba7 100644 --- a/apps/mobile/src/features/files/fileTree.test.ts +++ b/apps/mobile/src/features/files/fileTree.test.ts @@ -84,3 +84,17 @@ describe("mobile file tree helpers", () => { expect([...defaultExpandedTreePaths(tree)]).toEqual(["src"]); }); }); + +it("flattens a wide expanded directory without exceeding argument limits", () => { + const children = Array.from({ length: 200_000 }, (_, index) => ({ + path: `root/file-${index}`, + name: `file-${index}`, + kind: "file" as const, + children: [], + })); + const nodes = [{ path: "root", name: "root", kind: "directory" as const, children }]; + const rows = flattenFileTree({ nodes, expanded: new Set(["root"]) }); + expect(rows).toHaveLength(children.length + 1); + expect(rows[0]).toEqual({ node: nodes[0], depth: 0 }); + expect(rows.at(-1)).toEqual({ node: children.at(-1), depth: 1 }); +}); diff --git a/apps/mobile/src/features/files/fileTree.ts b/apps/mobile/src/features/files/fileTree.ts index 17b49d44ce41a..13a1ee7ae78ff 100644 --- a/apps/mobile/src/features/files/fileTree.ts +++ b/apps/mobile/src/features/files/fileTree.ts @@ -110,11 +110,12 @@ function flattenNode( const isSearching = searchResultPaths !== null; const matches = searchResultPaths?.has(node.path) ?? false; let descendantMatches = false; - const childOutput: VisibleFileTreeNode[] = []; + const start = output.length; + output.push({ node, depth }); if (node.kind === "directory" && (expanded.has(node.path) || isSearching)) { for (const child of node.children) { - if (flattenNode(childOutput, child, depth + 1, expanded, searchResultPaths)) { + if (flattenNode(output, child, depth + 1, expanded, searchResultPaths)) { descendantMatches = true; } } @@ -122,11 +123,10 @@ function flattenNode( const visible = !isSearching || matches || descendantMatches; if (!visible) { + output.length = start; return false; } - output.push({ node, depth }); - output.push(...childOutput); return matches || descendantMatches; } From 66a7da986caafce5cd9560420e709536344e2838 Mon Sep 17 00:00:00 2001 From: luke2x <97411838+luke2x@users.noreply.github.com> Date: Sun, 18 Oct 2026 21:19:30 +0100 Subject: [PATCH 2/3] test(mobile): assert interior child ordering in wide tree --- apps/mobile/src/features/files/fileTree.test.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/mobile/src/features/files/fileTree.test.ts b/apps/mobile/src/features/files/fileTree.test.ts index ec09414204ba7..f788e8072912a 100644 --- a/apps/mobile/src/features/files/fileTree.test.ts +++ b/apps/mobile/src/features/files/fileTree.test.ts @@ -96,5 +96,10 @@ it("flattens a wide expanded directory without exceeding argument limits", () => const rows = flattenFileTree({ nodes, expanded: new Set(["root"]) }); expect(rows).toHaveLength(children.length + 1); expect(rows[0]).toEqual({ node: nodes[0], depth: 0 }); + const middleChildIndex = Math.floor(children.length / 2); + expect(rows[middleChildIndex + 1]).toEqual({ + node: children[middleChildIndex], + depth: 1, + }); expect(rows.at(-1)).toEqual({ node: children.at(-1), depth: 1 }); }); From 317e35d85e9ce5c06a2c481e1d730db6b8c43379 Mon Sep 17 00:00:00 2001 From: maria-rcks Date: Mon, 26 Oct 2026 03:53:31 +0000 Subject: [PATCH 3/3] test(mobile): preserve search pruning in shared tree output --- .../src/features/files/fileTree.test.ts | 41 ++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/apps/mobile/src/features/files/fileTree.test.ts b/apps/mobile/src/features/files/fileTree.test.ts index 0ed2046076889..d978333d2732f 100644 --- a/apps/mobile/src/features/files/fileTree.test.ts +++ b/apps/mobile/src/features/files/fileTree.test.ts @@ -1,6 +1,6 @@ import { expect, it } from "vite-plus/test"; -import { flattenFileTree } from "./fileTree"; +import { buildFileTree, flattenFileTree } from "./fileTree"; it("flattens a wide expanded directory without exceeding argument limits", () => { const children = Array.from({ length: 200_000 }, (_, index) => ({ @@ -20,3 +20,42 @@ it("flattens a wide expanded directory without exceeding argument limits", () => }); expect(rows.at(-1)).toEqual({ node: children.at(-1), depth: 1 }); }); + +it("prunes unmatched subtrees without losing adjacent rows or their depths", () => { + const nodes = buildFileTree([ + { kind: "file", path: "root/a/hidden.txt" }, + { kind: "file", path: "root/b/match.txt" }, + { kind: "file", path: "root/b/other.txt" }, + { kind: "file", path: "root/c/hidden.txt" }, + { kind: "file", path: "tail.txt" }, + ]); + const expanded = new Set(["root", "root/a", "root/b", "root/c"]); + const rows = (searchResultPaths: ReadonlySet | null) => + flattenFileTree({ nodes, expanded, searchResultPaths }).map( + ({ node, depth }) => `${depth}:${node.path}`, + ); + + expect(rows(new Set(["root/b/match.txt", "tail.txt"]))).toEqual([ + "0:root", + "1:root/b", + "2:root/b/match.txt", + "0:tail.txt", + ]); + expect(rows(new Set(["root/b"]))).toEqual(["0:root", "1:root/b"]); + expect(rows(new Set())).toEqual([]); + expect(rows(null)).toEqual([ + "0:root", + "1:root/a", + "2:root/a/hidden.txt", + "1:root/b", + "2:root/b/match.txt", + "2:root/b/other.txt", + "1:root/c", + "2:root/c/hidden.txt", + "0:tail.txt", + ]); + expect(flattenFileTree({ nodes, expanded: new Set() }).map(({ node }) => node.path)).toEqual([ + "root", + "tail.txt", + ]); +});