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 <cursoragent@cursor.com>
This commit is contained in:
@@ -84,6 +84,11 @@ function isGrepLine(line) {
|
|||||||
function isPathLike(line) {
|
function isPathLike(line) {
|
||||||
const t = line.trim();
|
const t = line.trim();
|
||||||
if (t.length === 0) return false;
|
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;
|
if (t.includes(":")) return false;
|
||||||
return t.startsWith(".") || t.startsWith("/") || t.includes("/");
|
return t.startsWith(".") || t.startsWith("/") || t.includes("/");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,16 +9,17 @@ export function find(input) {
|
|||||||
const byDir = new Map();
|
const byDir = new Map();
|
||||||
|
|
||||||
for (const path of lines) {
|
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 dir;
|
||||||
let basename;
|
let basename;
|
||||||
if (lastSlash === -1) {
|
if (lastSep === -1) {
|
||||||
dir = ".";
|
dir = ".";
|
||||||
basename = path;
|
basename = path;
|
||||||
} else {
|
} else {
|
||||||
// Rust: PathBuf::from(path).parent().display() + file_name().display()
|
// Rust: PathBuf::from(path).parent().display() + file_name().display()
|
||||||
dir = path.slice(0, lastSlash) || "/";
|
dir = path.slice(0, lastSep) || "/";
|
||||||
basename = path.slice(lastSlash + 1);
|
basename = path.slice(lastSep + 1);
|
||||||
}
|
}
|
||||||
if (!byDir.has(dir)) byDir.set(dir, []);
|
if (!byDir.has(dir)) byDir.set(dir, []);
|
||||||
byDir.get(dir).push(basename);
|
byDir.get(dir).push(basename);
|
||||||
@@ -31,7 +32,8 @@ export function find(input) {
|
|||||||
const showDirs = dirs.slice(0, FIND_TOTAL_DIR_MAX);
|
const showDirs = dirs.slice(0, FIND_TOTAL_DIR_MAX);
|
||||||
for (const dir of showDirs) {
|
for (const dir of showDirs) {
|
||||||
const files = byDir.get(dir);
|
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);
|
const showFiles = files.slice(0, FIND_PER_DIR_MAX);
|
||||||
for (const f of showFiles) out += ` ${f}\n`;
|
for (const f of showFiles) out += ` ${f}\n`;
|
||||||
if (files.length > FIND_PER_DIR_MAX) {
|
if (files.length > FIND_PER_DIR_MAX) {
|
||||||
|
|||||||
62
tests/unit/rtkFindWindows.test.js
Normal file
62
tests/unit/rtkFindWindows.test.js
Normal file
@@ -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);
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user