refactor: wave 1 - extract leaf modules, rename catch-all files, split index.ts hooks
- Split 25+ index.ts files into hook.ts + extracted modules - Rename all catch-all utils.ts/helpers.ts to domain-specific names - Split src/tools/lsp/ into ~15 focused modules - Split src/tools/delegate-task/ into ~18 focused modules - Separate shared types from implementation - 155 files changed, 60+ new files created - All typecheck clean, 61 tests pass
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
import { extname, resolve } from "path"
|
||||
import { fileURLToPath } from "node:url"
|
||||
import { existsSync } from "fs"
|
||||
|
||||
import { LSPClient, lspManager } from "./client"
|
||||
import { findServerForExtension } from "./config"
|
||||
import type { ServerLookupResult } from "./types"
|
||||
|
||||
export function findWorkspaceRoot(filePath: string): string {
|
||||
let dir = resolve(filePath)
|
||||
|
||||
if (!existsSync(dir) || !require("fs").statSync(dir).isDirectory()) {
|
||||
dir = require("path").dirname(dir)
|
||||
}
|
||||
|
||||
const markers = [".git", "package.json", "pyproject.toml", "Cargo.toml", "go.mod", "pom.xml", "build.gradle"]
|
||||
|
||||
let prevDir = ""
|
||||
while (dir !== prevDir) {
|
||||
for (const marker of markers) {
|
||||
if (existsSync(require("path").join(dir, marker))) {
|
||||
return dir
|
||||
}
|
||||
}
|
||||
prevDir = dir
|
||||
dir = require("path").dirname(dir)
|
||||
}
|
||||
|
||||
return require("path").dirname(resolve(filePath))
|
||||
}
|
||||
|
||||
export function uriToPath(uri: string): string {
|
||||
return fileURLToPath(uri)
|
||||
}
|
||||
|
||||
export function formatServerLookupError(result: Exclude<ServerLookupResult, { status: "found" }>): string {
|
||||
if (result.status === "not_installed") {
|
||||
const { server, installHint } = result
|
||||
return [
|
||||
`LSP server '${server.id}' is configured but NOT INSTALLED.`,
|
||||
``,
|
||||
`Command not found: ${server.command[0]}`,
|
||||
``,
|
||||
`To install:`,
|
||||
` ${installHint}`,
|
||||
``,
|
||||
`Supported extensions: ${server.extensions.join(", ")}`,
|
||||
``,
|
||||
`After installation, the server will be available automatically.`,
|
||||
`Run 'LspServers' tool to verify installation status.`,
|
||||
].join("\n")
|
||||
}
|
||||
|
||||
return [
|
||||
`No LSP server configured for extension: ${result.extension}`,
|
||||
``,
|
||||
`Available servers: ${result.availableServers.slice(0, 10).join(", ")}${result.availableServers.length > 10 ? "..." : ""}`,
|
||||
``,
|
||||
`To add a custom server, configure 'lsp' in oh-my-opencode.json:`,
|
||||
` {`,
|
||||
` "lsp": {`,
|
||||
` "my-server": {`,
|
||||
` "command": ["my-lsp", "--stdio"],`,
|
||||
` "extensions": ["${result.extension}"]`,
|
||||
` }`,
|
||||
` }`,
|
||||
` }`,
|
||||
].join("\n")
|
||||
}
|
||||
|
||||
export async function withLspClient<T>(filePath: string, fn: (client: LSPClient) => Promise<T>): Promise<T> {
|
||||
const absPath = resolve(filePath)
|
||||
const ext = extname(absPath)
|
||||
const result = findServerForExtension(ext)
|
||||
|
||||
if (result.status !== "found") {
|
||||
throw new Error(formatServerLookupError(result))
|
||||
}
|
||||
|
||||
const server = result.server
|
||||
const root = findWorkspaceRoot(absPath)
|
||||
const client = await lspManager.getClient(root, server)
|
||||
|
||||
try {
|
||||
return await fn(client)
|
||||
} catch (e) {
|
||||
if (e instanceof Error && e.message.includes("timeout")) {
|
||||
const isInitializing = lspManager.isServerInitializing(root, server.id)
|
||||
if (isInitializing) {
|
||||
throw new Error(
|
||||
`LSP server is still initializing. Please retry in a few seconds. ` +
|
||||
`Original error: ${e.message}`
|
||||
)
|
||||
}
|
||||
}
|
||||
throw e
|
||||
} finally {
|
||||
lspManager.releaseClient(root, server.id)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user