Skip to content

Commit 83991be

Browse files
thdxrOpenCode
andcommitted
Add search function to fzf and move version constant to namespace level
🤖 Generated with [OpenCode](https://opencode.ai) Co-Authored-By: OpenCode <noreply@opencode.ai>
1 parent 29142eb commit 83991be

1 file changed

Lines changed: 134 additions & 0 deletions

File tree

  • packages/opencode/src/external
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import { App } from "../app/app"
2+
import path from "path"
3+
import { Global } from "../global"
4+
import fs from "fs/promises"
5+
import { z } from "zod"
6+
import { NamedError } from "../util/error"
7+
import { lazy } from "../util/lazy"
8+
import { Log } from "../util/log"
9+
10+
export namespace Fzf {
11+
const log = Log.create({ service: "fzf" })
12+
13+
const VERSION = "0.62.0"
14+
const PLATFORM = {
15+
darwin: { extension: "tar.gz" },
16+
linux: { extension: "tar.gz" },
17+
win32: { extension: "zip" },
18+
} as const
19+
20+
export const ExtractionFailedError = NamedError.create(
21+
"FzfExtractionFailedError",
22+
z.object({
23+
filepath: z.string(),
24+
stderr: z.string(),
25+
}),
26+
)
27+
28+
export const UnsupportedPlatformError = NamedError.create(
29+
"FzfUnsupportedPlatformError",
30+
z.object({
31+
platform: z.string(),
32+
}),
33+
)
34+
35+
export const DownloadFailedError = NamedError.create(
36+
"FzfDownloadFailedError",
37+
z.object({
38+
url: z.string(),
39+
status: z.number(),
40+
}),
41+
)
42+
43+
const state = lazy(async () => {
44+
let filepath = Bun.which("fzf")
45+
if (filepath) {
46+
log.info("found", { filepath })
47+
return { filepath }
48+
}
49+
filepath = path.join(
50+
Global.Path.bin,
51+
"fzf" + (process.platform === "win32" ? ".exe" : ""),
52+
)
53+
54+
const file = Bun.file(filepath)
55+
if (!(await file.exists())) {
56+
const archMap = { x64: "amd64", arm64: "arm64" } as const
57+
const arch = archMap[process.arch as keyof typeof archMap] ?? "amd64"
58+
59+
const config = PLATFORM[process.platform as keyof typeof PLATFORM]
60+
if (!config)
61+
throw new UnsupportedPlatformError({ platform: process.platform })
62+
63+
const version = VERSION
64+
const platformName =
65+
process.platform === "win32" ? "windows" : process.platform
66+
const filename = `fzf-${version}-${platformName}_${arch}.${config.extension}`
67+
const url = `https://github.com/junegunn/fzf/releases/download/v${version}/${filename}`
68+
69+
const response = await fetch(url)
70+
if (!response.ok)
71+
throw new DownloadFailedError({ url, status: response.status })
72+
73+
const buffer = await response.arrayBuffer()
74+
const archivePath = path.join(Global.Path.bin, filename)
75+
await Bun.write(archivePath, buffer)
76+
if (config.extension === "tar.gz") {
77+
const proc = Bun.spawn(["tar", "-xzf", archivePath, "fzf"], {
78+
cwd: Global.Path.bin,
79+
stderr: "pipe",
80+
stdout: "pipe",
81+
})
82+
await proc.exited
83+
if (proc.exitCode !== 0)
84+
throw new ExtractionFailedError({
85+
filepath,
86+
stderr: await Bun.readableStreamToText(proc.stderr),
87+
})
88+
}
89+
if (config.extension === "zip") {
90+
const proc = Bun.spawn(
91+
["unzip", "-j", archivePath, "fzf.exe", "-d", Global.Path.bin],
92+
{
93+
cwd: Global.Path.bin,
94+
stderr: "pipe",
95+
stdout: "ignore",
96+
},
97+
)
98+
await proc.exited
99+
if (proc.exitCode !== 0)
100+
throw new ExtractionFailedError({
101+
filepath: archivePath,
102+
stderr: await Bun.readableStreamToText(proc.stderr),
103+
})
104+
}
105+
await fs.unlink(archivePath)
106+
if (process.platform !== "win32") await fs.chmod(filepath, 0o755)
107+
}
108+
109+
return {
110+
filepath,
111+
}
112+
})
113+
114+
export async function filepath() {
115+
const { filepath } = await state()
116+
return filepath
117+
}
118+
119+
export async function search(cwd: string, query: string) {
120+
const process = Bun.spawn({
121+
cwd,
122+
stdin: "inherit",
123+
stdout: "pipe",
124+
stderr: "pipe",
125+
cmd: [await filepath(), "--filter", query],
126+
})
127+
await process.exited
128+
const stdout = await Bun.readableStreamToText(process.stdout)
129+
return stdout
130+
.trim()
131+
.split("\n")
132+
.filter((line) => line.length > 0)
133+
}
134+
}

0 commit comments

Comments
 (0)