|
| 1 | +import { Effect } from "effect" |
| 2 | +import { Fff } from "#fff" |
| 3 | +import { AppRuntime } from "@/effect/app-runtime" |
| 4 | +import { Search } from "@/file/search" |
| 5 | +import { InstanceStore } from "@/project/instance-store" |
| 6 | + |
| 7 | +const dir = process.cwd() |
| 8 | + |
| 9 | +const FILE_QUERIES = ["fff", "package.json", "tools/ experiment"] |
| 10 | +const GREP_QUERIES = ["FileFinder", "import", "grep", "autocomplete"] |
| 11 | +const GLOB_QUERIES = ["**/*.test.ts"] |
| 12 | + |
| 13 | +const FILE_LIMIT = 100 |
| 14 | +const GREP_LIMIT = 50 |
| 15 | +const GLOB_LIMIT = 50 |
| 16 | + |
| 17 | +const run = <A>(effect: Effect.Effect<A, unknown, Search.Service>) => |
| 18 | + AppRuntime.runPromise( |
| 19 | + InstanceStore.Service.use((store) => store.provide({ directory: dir }, effect as never)), |
| 20 | + ) as Promise<A> |
| 21 | + |
| 22 | +// --- raw Fff picker --- |
| 23 | +const t0 = performance.now() |
| 24 | +const made = Fff.create({ basePath: dir, aiMode: true }) |
| 25 | +if (!made.ok) { |
| 26 | + console.error("Fff.create failed:", made.error) |
| 27 | + process.exit(1) |
| 28 | +} |
| 29 | +const picker = made.value |
| 30 | +console.log(`picker create: ${(performance.now() - t0).toFixed(1)}ms`) |
| 31 | + |
| 32 | +const tw = performance.now() |
| 33 | +const deadline = tw + 2500 |
| 34 | +while (picker.isScanning() && performance.now() < deadline) { |
| 35 | + await new Promise((resolve) => setTimeout(resolve, 25)) |
| 36 | +} |
| 37 | +console.log(`wait for scan (poll): ${(performance.now() - tw).toFixed(1)}ms`) |
| 38 | + |
| 39 | +// warmup grep to let the content index build |
| 40 | +const tWarmup = performance.now() |
| 41 | +picker.grep("_warmup_", { mode: "regex", maxMatchesPerFile: 1, timeBudgetMs: 1_500 }) |
| 42 | +console.log(`grep warmup: ${(performance.now() - tWarmup).toFixed(1)}ms`) |
| 43 | + |
| 44 | +console.log() |
| 45 | +console.log("--- raw picker (warm) ---") |
| 46 | + |
| 47 | +for (const q of FILE_QUERIES) { |
| 48 | + const t = performance.now() |
| 49 | + const r = picker.fileSearch(q, { pageSize: Math.max(FILE_LIMIT, 100) }) |
| 50 | + const count = r.ok ? r.value.items.length : "err" |
| 51 | + console.log(`[picker] fileSearch "${q}": ${(performance.now() - t).toFixed(1)}ms (${count} results)`) |
| 52 | +} |
| 53 | + |
| 54 | +for (const q of GREP_QUERIES) { |
| 55 | + const t = performance.now() |
| 56 | + const r = picker.grep(q, { mode: "regex", pageSize: GREP_LIMIT, timeBudgetMs: 1_500 }) |
| 57 | + const count = r.ok ? r.value.items.length : "err" |
| 58 | + console.log(`[picker] grep "${q}": ${(performance.now() - t).toFixed(1)}ms (${count} matches)`) |
| 59 | +} |
| 60 | + |
| 61 | +picker.destroy() |
| 62 | + |
| 63 | +// --- Ripgrep service (via Search with file:["."] to force rg path) --- |
| 64 | +console.log() |
| 65 | +console.log("--- Ripgrep (via Search service) ---") |
| 66 | + |
| 67 | +// warmup |
| 68 | +await run(Search.Service.use((svc) => svc.search({ cwd: dir, pattern: "_warmup_rg_", limit: 1, file: ["."] }))) |
| 69 | + |
| 70 | +for (const q of GREP_QUERIES) { |
| 71 | + const t = performance.now() |
| 72 | + const r = await run(Search.Service.use((svc) => svc.search({ cwd: dir, pattern: q, limit: GREP_LIMIT, file: ["."] }))) |
| 73 | + console.log( |
| 74 | + `[ripgrep] grep "${q}": ${(performance.now() - t).toFixed(1)}ms (${r.items.length} total, limit is per-file not total)`, |
| 75 | + ) |
| 76 | +} |
| 77 | + |
| 78 | +// --- Search service: init breakdown --- |
| 79 | +console.log() |
| 80 | + |
| 81 | +// 1) runtime + InstanceState + picker create + scan poll |
| 82 | +const tRuntime = performance.now() |
| 83 | +await run(Search.Service.use((svc) => svc.file({ cwd: dir, query: "_warmup_file_", limit: 1 }))) |
| 84 | +console.log(`[Search] init file (runtime + picker + scan): ${(performance.now() - tRuntime).toFixed(1)}ms`) |
| 85 | + |
| 86 | +// 2) grep warmup (content index cold-start inside the Search service picker) |
| 87 | +const tGrepWarmup = performance.now() |
| 88 | +await run(Search.Service.use((svc) => svc.search({ cwd: dir, pattern: "_warmup_grep_", limit: 1 }))) |
| 89 | +console.log(`[Search] init grep (content index warmup): ${(performance.now() - tGrepWarmup).toFixed(1)}ms`) |
| 90 | + |
| 91 | +console.log() |
| 92 | +console.log("--- Search service (warm) ---") |
| 93 | + |
| 94 | +for (const q of FILE_QUERIES) { |
| 95 | + const t = performance.now() |
| 96 | + const r = await run(Search.Service.use((svc) => svc.file({ cwd: dir, query: q, limit: FILE_LIMIT }))) |
| 97 | + console.log( |
| 98 | + `[Search.file] "${q}": ${(performance.now() - t).toFixed(1)}ms (${r?.length ?? "undefined (cache fallback)"} results)`, |
| 99 | + ) |
| 100 | +} |
| 101 | + |
| 102 | +for (const q of GREP_QUERIES) { |
| 103 | + const t = performance.now() |
| 104 | + const r = await run(Search.Service.use((svc) => svc.search({ cwd: dir, pattern: q, limit: GREP_LIMIT }))) |
| 105 | + console.log( |
| 106 | + `[Search.search] "${q}": ${(performance.now() - t).toFixed(1)}ms (${r.items.length} matches, engine=${r.engine})`, |
| 107 | + ) |
| 108 | +} |
| 109 | + |
| 110 | +for (const q of GLOB_QUERIES) { |
| 111 | + const t = performance.now() |
| 112 | + const r = await run(Search.Service.use((svc) => svc.glob({ cwd: dir, pattern: q, limit: GLOB_LIMIT }))) |
| 113 | + console.log( |
| 114 | + `[Search.glob] "${q}": ${(performance.now() - t).toFixed(1)}ms (${r.files.length} files, truncated=${r.truncated})`, |
| 115 | + ) |
| 116 | +} |
| 117 | + |
| 118 | +process.exit(0) |
| 119 | + |
0 commit comments