sync
parent
8ea1df1560
commit
8056842baf
|
|
@ -1,23 +1,19 @@
|
|||
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
|
||||
})
|
||||
|
||||
const init = (cwd: string) => {
|
||||
const result = FileFinder.init({ basePath: cwd })
|
||||
if (result.ok) return true
|
||||
log.error("init failed", { error: result.error, cwd })
|
||||
return false
|
||||
}
|
||||
|
||||
export async function search(input: { cwd: string; query: string; limit: number }) {
|
||||
if (!input.query) return []
|
||||
if (!base) base = input.cwd
|
||||
if (!init()) return []
|
||||
if (!init(input.cwd)) return []
|
||||
|
||||
const result = FileFinder.search(input.query, {
|
||||
pageIndex: 0,
|
||||
|
|
|
|||
|
|
@ -548,43 +548,55 @@ export namespace File {
|
|||
const kind = input.type ?? "all"
|
||||
log.info("search", { query, kind })
|
||||
|
||||
if (!query) {
|
||||
const result = await state().then((x) => x.files())
|
||||
if (kind === "file") return result.files.slice(0, limit)
|
||||
return result.dirs.toSorted().slice(0, limit)
|
||||
const files = await FFF.search({
|
||||
cwd: Instance.directory,
|
||||
query,
|
||||
limit: kind === "all" || kind === "directory" ? limit * 20 : limit,
|
||||
})
|
||||
const set = new Set<string>()
|
||||
for (const file of files) {
|
||||
let dir = path.dirname(file)
|
||||
while (true) {
|
||||
if (dir === ".") break
|
||||
const next = path.dirname(dir)
|
||||
set.add(dir + "/")
|
||||
if (next === dir) break
|
||||
dir = next
|
||||
}
|
||||
}
|
||||
const allDirs = Array.from(set)
|
||||
|
||||
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)
|
||||
if (!query) {
|
||||
const output = kind === "file" ? files.slice(0, limit) : allDirs.toSorted().slice(0, limit)
|
||||
log.info("search", { query, kind, results: output.length })
|
||||
return output
|
||||
}
|
||||
|
||||
if (kind === "directory") {
|
||||
const ranked: string[] = []
|
||||
for (const item of fuzzysort.go(query, allDirs, { limit: limit * 20 })) {
|
||||
ranked.push(item.target)
|
||||
}
|
||||
const output = ranked.slice(0, limit)
|
||||
log.info("search", { query, kind, results: output.length })
|
||||
return output
|
||||
}
|
||||
|
||||
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 output = files.slice(0, limit)
|
||||
log.info("search", { query, kind, results: output.length })
|
||||
return output
|
||||
}
|
||||
|
||||
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 rankedDirs: string[] = []
|
||||
for (const item of fuzzysort.go(query, allDirs, { limit })) {
|
||||
rankedDirs.push(item.target)
|
||||
}
|
||||
const merged = files.slice(0, limit).concat(rankedDirs)
|
||||
const output: string[] = []
|
||||
for (const item of fuzzysort.go(query, merged, { limit })) {
|
||||
output.push(item.target)
|
||||
}
|
||||
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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
import { describe, expect, test } from "bun:test"
|
||||
import path from "path"
|
||||
import { tmpdir } from "../fixture/fixture"
|
||||
import { FFF } from "../../src/file/fff"
|
||||
import { File } from "../../src/file"
|
||||
import { Instance } from "../../src/project/instance"
|
||||
|
||||
describe("file.fff", () => {
|
||||
test("returns files and supports directory search via File.search", async () => {
|
||||
await using tmp = await tmpdir({
|
||||
init: async (dir) => {
|
||||
await Bun.write(path.join(dir, "src", "app", "index.ts"), "export const app = true")
|
||||
await Bun.write(path.join(dir, "src", "app", "util.ts"), "export const util = true")
|
||||
await Bun.write(path.join(dir, "docs", "guide.md"), "# guide")
|
||||
},
|
||||
})
|
||||
|
||||
const files = await FFF.search({
|
||||
cwd: tmp.path,
|
||||
query: "index",
|
||||
limit: 20,
|
||||
})
|
||||
expect(files.includes(path.join("src", "app", "index.ts"))).toBe(true)
|
||||
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const found = await File.search({
|
||||
query: "index",
|
||||
type: "file",
|
||||
limit: 20,
|
||||
})
|
||||
expect(found.includes(path.join("src", "app", "index.ts"))).toBe(true)
|
||||
|
||||
const dirs = await File.search({
|
||||
query: "app",
|
||||
type: "directory",
|
||||
limit: 20,
|
||||
})
|
||||
expect(dirs.includes("src/app/")).toBe(true)
|
||||
|
||||
const all = await File.search({
|
||||
query: "app",
|
||||
type: "all",
|
||||
limit: 20,
|
||||
})
|
||||
expect(all.includes(path.join("src", "app", "index.ts"))).toBe(true)
|
||||
expect(all.includes("src/app/")).toBe(true)
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
Loading…
Reference in New Issue