Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/opencode/src/flag/flag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export namespace Flag {
export const OPENCODE_EXPERIMENTAL_OUTPUT_TOKEN_MAX = number("OPENCODE_EXPERIMENTAL_OUTPUT_TOKEN_MAX")
export const OPENCODE_EXPERIMENTAL_OXFMT = OPENCODE_EXPERIMENTAL || truthy("OPENCODE_EXPERIMENTAL_OXFMT")
export const OPENCODE_EXPERIMENTAL_LSP_TY = truthy("OPENCODE_EXPERIMENTAL_LSP_TY")
export const OPENCODE_EXPERIMENTAL_LSP_RUFF = truthy("OPENCODE_EXPERIMENTAL_LSP_RUFF")
export const OPENCODE_EXPERIMENTAL_LSP_TOOL = OPENCODE_EXPERIMENTAL || truthy("OPENCODE_EXPERIMENTAL_LSP_TOOL")
export const OPENCODE_EXPERIMENTAL_PLAN_MODE = OPENCODE_EXPERIMENTAL || truthy("OPENCODE_EXPERIMENTAL_PLAN_MODE")

Expand Down
29 changes: 19 additions & 10 deletions packages/opencode/src/lsp/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,17 +62,26 @@ export namespace LSP {
export type DocumentSymbol = z.infer<typeof DocumentSymbol>

const filterExperimentalServers = (servers: Record<string, LSPServer.Info>) => {
const disable = (id: string, reason?: string) => {
if (!servers[id]) return
if (reason) log.info(reason)
delete servers[id]
}

if (Flag.OPENCODE_EXPERIMENTAL_LSP_TY) {
// If experimental flag is enabled, disable pyright
if (servers["pyright"]) {
log.info("LSP server pyright is disabled because OPENCODE_EXPERIMENTAL_LSP_TY is enabled")
delete servers["pyright"]
}
} else {
// If experimental flag is disabled, disable ty
if (servers["ty"]) {
delete servers["ty"]
}
disable("pyright", "LSP server pyright is disabled because OPENCODE_EXPERIMENTAL_LSP_TY is enabled")
}

if (!Flag.OPENCODE_EXPERIMENTAL_LSP_TY) {
disable("ty")
}

if (Flag.OPENCODE_EXPERIMENTAL_LSP_RUFF) {
disable("pyright", "LSP server pyright is disabled because OPENCODE_EXPERIMENTAL_LSP_RUFF is enabled")
}

if (!Flag.OPENCODE_EXPERIMENTAL_LSP_RUFF) {
disable("ruff")
}
}

Expand Down
51 changes: 51 additions & 0 deletions packages/opencode/src/lsp/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,57 @@ export namespace LSPServer {
},
}

export const Ruff: Info = {
id: "ruff",
extensions: [".py", ".pyi"],
root: NearestRoot([
"pyproject.toml",
"ruff.toml",
".ruff.toml",
"setup.py",
"setup.cfg",
"requirements.txt",
"Pipfile",
]),
async spawn(root) {
if (!Flag.OPENCODE_EXPERIMENTAL_LSP_RUFF) {
return undefined
}

const venvs = [process.env["VIRTUAL_ENV"], path.join(root, ".venv"), path.join(root, "venv")].filter(
(p): p is string => p !== undefined,
)

const binary = await (async () => {
const direct = Bun.which("ruff")
if (direct) return direct

for (const venv of venvs) {
const windows = process.platform === "win32"
const candidate = windows ? path.join(venv, "Scripts", "ruff.exe") : path.join(venv, "bin", "ruff")
if (await Bun.file(candidate).exists()) {
return candidate
}
}

return undefined
})()

if (!binary) {
log.error("ruff not found, please install ruff first")
return
}

const proc = spawn(binary, ["server"], {
cwd: root,
})

return {
process: proc,
}
},
}

export const Pyright: Info = {
id: "pyright",
extensions: [".py", ".pyi"],
Expand Down