feat(file): use FFF for file searches
This commit is contained in:
parent
c6ec2f47ef
commit
3c11eda350
4 changed files with 93 additions and 24 deletions
|
|
@ -71,6 +71,7 @@
|
|||
"@ai-sdk/vercel": "1.0.33",
|
||||
"@ai-sdk/xai": "2.0.51",
|
||||
"@clack/prompts": "1.0.0-alpha.1",
|
||||
"@ff-labs/bun": "0.1.37",
|
||||
"@gitlab/gitlab-ai-provider": "3.5.0",
|
||||
"@gitlab/opencode-gitlab-auth": "1.3.2",
|
||||
"@hono/standard-validator": "0.1.5",
|
||||
|
|
|
|||
33
packages/opencode/src/file/fff.ts
Normal file
33
packages/opencode/src/file/fff.ts
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import { FileFinder } from "@ff-labs/bun"
|
||||
import { Log } from "@/util/log"
|
||||
import { lazy } from "../util/lazy"
|
||||
|
||||
export namespace FFF {
|
||||
const log = Log.create({ service: "file.fff" })
|
||||
let base = ""
|
||||
const init = lazy(() => {
|
||||
const result = FileFinder.init({ basePath: base })
|
||||
if (!result.ok) {
|
||||
log.error("init failed", { error: result.error, cwd: base })
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
export async function search(input: { cwd: string; query: string; limit: number }) {
|
||||
if (!input.query) return []
|
||||
if (!base) base = input.cwd
|
||||
if (!init()) return []
|
||||
|
||||
const result = FileFinder.search(input.query, {
|
||||
pageIndex: 0,
|
||||
pageSize: input.limit,
|
||||
})
|
||||
if (!result.ok) {
|
||||
log.error("search failed", { error: result.error, query: input.query, cwd: input.cwd })
|
||||
return []
|
||||
}
|
||||
|
||||
return result.value.items.map((item: { relativePath: string }) => item.relativePath)
|
||||
}
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@ import ignore from "ignore"
|
|||
import { Log } from "../util/log"
|
||||
import { Filesystem } from "../util/filesystem"
|
||||
import { Instance } from "../project/instance"
|
||||
import { FFF } from "./fff"
|
||||
import { Ripgrep } from "./ripgrep"
|
||||
import fuzzysort from "fuzzysort"
|
||||
import { Global } from "../global"
|
||||
|
|
@ -547,35 +548,42 @@ export namespace File {
|
|||
const kind = input.type ?? (input.dirs === false ? "file" : "all")
|
||||
log.info("search", { query, kind })
|
||||
|
||||
const result = await state().then((x) => x.files())
|
||||
|
||||
const hidden = (item: string) => {
|
||||
const normalized = item.replaceAll("\\", "/").replace(/\/+$/, "")
|
||||
return normalized.split("/").some((p) => p.startsWith(".") && p.length > 1)
|
||||
}
|
||||
const preferHidden = query.startsWith(".") || query.includes("/.")
|
||||
const sortHiddenLast = (items: string[]) => {
|
||||
if (preferHidden) return items
|
||||
const visible: string[] = []
|
||||
const hiddenItems: string[] = []
|
||||
for (const item of items) {
|
||||
const isHidden = hidden(item)
|
||||
if (isHidden) hiddenItems.push(item)
|
||||
if (!isHidden) visible.push(item)
|
||||
}
|
||||
return [...visible, ...hiddenItems]
|
||||
}
|
||||
if (!query) {
|
||||
const result = await state().then((x) => x.files())
|
||||
if (kind === "file") return result.files.slice(0, limit)
|
||||
return sortHiddenLast(result.dirs.toSorted()).slice(0, limit)
|
||||
return result.dirs.toSorted().slice(0, limit)
|
||||
}
|
||||
|
||||
const items =
|
||||
kind === "file" ? result.files : kind === "directory" ? result.dirs : [...result.files, ...result.dirs]
|
||||
if (kind === "directory") {
|
||||
const result = await state().then((x) => x.files())
|
||||
const searchLimit = limit * 20
|
||||
const output = fuzzysort
|
||||
.go(query, result.dirs, { limit: searchLimit })
|
||||
.map((r) => r.target)
|
||||
.slice(0, limit)
|
||||
log.info("search", { query, kind, results: output.length })
|
||||
return output
|
||||
}
|
||||
|
||||
const searchLimit = kind === "directory" && !preferHidden ? limit * 20 : limit
|
||||
const sorted = fuzzysort.go(query, items, { limit: searchLimit }).map((r) => r.target)
|
||||
const output = kind === "directory" ? sortHiddenLast(sorted).slice(0, limit) : sorted
|
||||
const files = await FFF.search({
|
||||
cwd: Instance.directory,
|
||||
query,
|
||||
limit,
|
||||
})
|
||||
const fileOutput = files.slice(0, limit)
|
||||
if (kind === "file") {
|
||||
log.info("search", { query, kind, results: fileOutput.length })
|
||||
return fileOutput
|
||||
}
|
||||
|
||||
const result = await state().then((x) => x.files())
|
||||
const remaining = limit - fileOutput.length
|
||||
if (remaining <= 0) {
|
||||
log.info("search", { query, kind, results: fileOutput.length })
|
||||
return fileOutput
|
||||
}
|
||||
const sorted = fuzzysort.go(query, result.dirs, { limit: remaining }).map((r) => r.target)
|
||||
const output = fileOutput.concat(sorted)
|
||||
|
||||
log.info("search", { query, kind, results: output.length })
|
||||
return output
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue