From d75471bbbcfbf761b7c6e3ab619a66200ff0901d Mon Sep 17 00:00:00 2001 From: Diwak4r Date: Thu, 9 Jul 2026 15:09:41 +0700 Subject: [PATCH] fix(rtk/find): detect and group Windows backslash-style find output (#2448) isPathLike rejected any line with a colon, so Windows absolute paths (C:\Users\me\a.js) were never recognized and find dumps went uncompacted. find.js also split only on "/", mis-grouping backslash paths. - autodetect: treat drive-letter prefix (X:\ or X:/) as path-like before the general colon rejection. - find.js: split on the last "/" or "\" separator and normalize emitted directory labels to forward slashes. Co-authored-by: Cursor --- open-sse/rtk/autodetect.js | 5 +++ open-sse/rtk/filters/find.js | 12 +++--- tests/unit/rtkFindWindows.test.js | 62 +++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 5 deletions(-) create mode 100644 tests/unit/rtkFindWindows.test.js diff --git a/open-sse/rtk/autodetect.js b/open-sse/rtk/autodetect.js index 8bc4356c..81992034 100644 --- a/open-sse/rtk/autodetect.js +++ b/open-sse/rtk/autodetect.js @@ -84,6 +84,11 @@ function isGrepLine(line) { function isPathLike(line) { const t = line.trim(); if (t.length === 0) return false; + // A drive-letter prefix (e.g. "C:\Users\me" or "C:/Users/me") marks a + // Windows absolute path, so treat the whole line as path-like. Trailing + // colons (e.g. "C:\path\file.js:10") are tolerated, matching grep-style + // suffixes on Windows dumps. + if (/^[A-Za-z]:[\\/]/.test(t)) return true; if (t.includes(":")) return false; return t.startsWith(".") || t.startsWith("/") || t.includes("/"); } diff --git a/open-sse/rtk/filters/find.js b/open-sse/rtk/filters/find.js index 5770a99b..4b64169b 100644 --- a/open-sse/rtk/filters/find.js +++ b/open-sse/rtk/filters/find.js @@ -9,16 +9,17 @@ export function find(input) { const byDir = new Map(); for (const path of lines) { - const lastSlash = path.lastIndexOf("/"); + // Accept both Unix ("/a/b") and Windows ("C:\a\b") separators + const lastSep = Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\")); let dir; let basename; - if (lastSlash === -1) { + if (lastSep === -1) { dir = "."; basename = path; } else { // Rust: PathBuf::from(path).parent().display() + file_name().display() - dir = path.slice(0, lastSlash) || "/"; - basename = path.slice(lastSlash + 1); + dir = path.slice(0, lastSep) || "/"; + basename = path.slice(lastSep + 1); } if (!byDir.has(dir)) byDir.set(dir, []); byDir.get(dir).push(basename); @@ -31,7 +32,8 @@ export function find(input) { const showDirs = dirs.slice(0, FIND_TOTAL_DIR_MAX); for (const dir of showDirs) { const files = byDir.get(dir); - out += `${dir}/ (${files.length})\n`; + const dirLabel = dir.replace(/\\/g, "/"); + out += `${dirLabel}/ (${files.length})\n`; const showFiles = files.slice(0, FIND_PER_DIR_MAX); for (const f of showFiles) out += ` ${f}\n`; if (files.length > FIND_PER_DIR_MAX) { diff --git a/tests/unit/rtkFindWindows.test.js b/tests/unit/rtkFindWindows.test.js new file mode 100644 index 00000000..05b7b58e --- /dev/null +++ b/tests/unit/rtkFindWindows.test.js @@ -0,0 +1,62 @@ +// Tests for Windows path support in the `find` filter + autodetect +// Windows absolute paths ("C:\Users\me\src\a.js") carry a drive-letter +// separator that the Unix-only colon check used to reject, so no compaction +// happened for Windows `find`-style dumps. See fix(rtk/find). +import { describe, it, expect } from "vitest"; +import { autoDetectFilter } from "../../open-sse/rtk/autodetect.js"; +import { find } from "../../open-sse/rtk/filters/find.js"; +import { grep } from "../../open-sse/rtk/filters/grep.js"; + +const WIN_PATHS = [ + "C:\\Users\\me\\project\\src\\a.js", + "C:\\Users\\me\\project\\src\\b.js", + "C:\\Users\\me\\project\\src\\c.js" +].join("\n"); + +const UNIX_PATHS = [ + "./src/a.js", + "./src/b.js", + "./src/c.js" +].join("\n"); + +describe("Windows find-path detection", () => { + it("detects Windows drive-letter paths as `find`", () => { + expect(autoDetectFilter(WIN_PATHS)).toBe(find); + }); + + it("still detects Unix paths as `find` (no regression)", () => { + expect(autoDetectFilter(UNIX_PATHS)).toBe(find); + }); + + it("still routes a Windows file:line dump to a compacting filter", () => { + const input = [ + "C:\\Users\\me\\project\\src\\a.js:10:const x = 1", + "C:\\Users\\me\\project\\src\\b.js:20:const y = 2", + "C:\\Users\\me\\project\\src\\c.js:30:const z = 3" + ].join("\n"); + // Each line is grep-shaped (file:line:content), so it routes to `grep` + // — but a drive-letter-only dump would route to `find`. Both are + // compaction-positive, so either is acceptable here. + const f = autoDetectFilter(input); + expect(f).not.toBeNull(); + expect([find, grep]).toContain(f); + }); +}); + +describe("Windows find-path grouping", () => { + it("groups Windows backslash paths and normalizes to forward slashes", () => { + const out = find(WIN_PATHS); + expect(out).toContain("3 files in 1 dirs"); + expect(out).toContain("C:/Users/me/project/src/"); + expect(out).toContain("a.js"); + expect(out).toContain("b.js"); + expect(out).toContain("c.js"); + // backslashes must not leak into output + expect(out).not.toContain("\\"); + }); + + it("compresses the dump (output shorter than input)", () => { + const out = find(WIN_PATHS); + expect(out.length).toBeLessThan(WIN_PATHS.length); + }); +});