From 9d5ab28f7647213170ba185571334a2a47399631 Mon Sep 17 00:00:00 2001 From: LukeParkerDev <10430890+Hona@users.noreply.github.com> Date: Thu, 30 Apr 2026 10:21:48 +1000 Subject: [PATCH] align --- packages/opencode/src/file/search.ts | 22 ++++------ packages/opencode/test/file/search.test.ts | 47 ++++++++++++++++++++++ 2 files changed, 54 insertions(+), 15 deletions(-) diff --git a/packages/opencode/src/file/search.ts b/packages/opencode/src/file/search.ts index 81c36cf88..11ac24493 100644 --- a/packages/opencode/src/file/search.ts +++ b/packages/opencode/src/file/search.ts @@ -127,14 +127,8 @@ function remember(state: State, dir: string, text: string, files: string[]) { if (state.recent.length > 32) state.recent.length = 32 } -function matchesTokens(query: string, file: string) { - const tokens = query.toLowerCase().split(/[^a-z0-9]+/).filter(Boolean) - if (!tokens.length) return true - const normalized = normalize(file).toLowerCase() - return tokens.every((token) => normalized.includes(token)) -} - function item(hit: Fff.Hit): Item { + const line = Buffer.from(hit.lineContent) return { path: { text: normalize(hit.relativePath) }, lines: { text: hit.lineContent }, @@ -142,7 +136,7 @@ function item(hit: Fff.Hit): Item { absolute_offset: hit.byteOffset, submatches: hit.matchRanges .map(([start, end]) => { - const text = hit.lineContent.slice(start, end) + const text = line.subarray(start, end).toString("utf8") if (!text) return undefined return { match: { text }, @@ -281,15 +275,14 @@ export const layer: Layer.Layer= 4 ? rows.filter((row) => matchesTokens(query, row)) : rows const current = yield* InstanceState.get(state) remember( current, dir, query, - output.map((row) => path.join(dir, row)), + rows.map((row) => path.join(dir, row)), ) - return output.slice(0, input.limit ?? 100) + return rows.slice(0, input.limit ?? 100) }) const search: Interface["search"] = Effect.fn("Search.search")(function* (input) { @@ -300,19 +293,18 @@ export const layer: Layer.Layer() let cursor: Fff.Cursor = null let regexFallbackError: string | undefined - while (rows.length < limit) { + while (input.limit === undefined || rows.length < input.limit) { input.signal?.throwIfAborted() const out = yield* Effect.sync(() => pick.grep(input.pattern, { mode: "regex", cursor, - maxMatchesPerFile: limit, + maxMatchesPerFile: input.limit ?? 0, timeBudgetMs: 1_500, }), ) @@ -329,7 +321,7 @@ export const layer: Layer.Layer= limit) break + if (input.limit !== undefined && rows.length >= input.limit) break } if (!out.value.nextCursor) break diff --git a/packages/opencode/test/file/search.test.ts b/packages/opencode/test/file/search.test.ts index 1858209b7..5c01c0bd1 100644 --- a/packages/opencode/test/file/search.test.ts +++ b/packages/opencode/test/file/search.test.ts @@ -41,6 +41,53 @@ describe("file.search", () => { ), ) + it.live("keeps fuzzy file abbreviation matches", () => + provideTmpdirInstance((dir) => + Effect.gen(function* () { + expect(Fff.available()).toBe(true) + yield* Effect.promise(() => Bun.write(path.join(dir, "README.md"), "hello\n")) + + const search = yield* Search.Service + const results = yield* search.file({ cwd: dir, query: "rdme", limit: 10 }) + + expect(results).toContain("README.md") + }), + ), + ) + + it.live("keeps paging grep results without an explicit limit", () => + provideTmpdirInstance((dir) => + Effect.gen(function* () { + expect(Fff.available()).toBe(true) + yield* Effect.promise(() => + Bun.write( + path.join(dir, "matches.txt"), + Array.from({ length: 150 }, (_, idx) => `needle ${idx}\n`).join(""), + ), + ) + + const search = yield* Search.Service + const result = yield* search.search({ cwd: dir, pattern: "needle" }) + + expect(result.items).toHaveLength(150) + }), + ), + ) + + it.live("uses byte ranges for UTF-8 grep submatches", () => + provideTmpdirInstance((dir) => + Effect.gen(function* () { + expect(Fff.available()).toBe(true) + yield* Effect.promise(() => Bun.write(path.join(dir, "unicode.txt"), "éneedle\n")) + + const search = yield* Search.Service + const result = yield* search.search({ cwd: dir, pattern: "needle", limit: 10 }) + + expect(result.items[0]?.submatches[0]?.match.text).toBe("needle") + }), + ), + ) + it.live("records query history when a searched file is opened", () => provideTmpdirInstance((dir) => Effect.gen(function* () {