mirror of
https://gitea.toothfairyai.com/ToothFairyAI/tf_code.git
synced 2026-04-02 15:13:46 +00:00
feat: add experimental lsp tool (#5886)
This commit is contained in:
87
packages/opencode/src/tool/lsp.ts
Normal file
87
packages/opencode/src/tool/lsp.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
import z from "zod"
|
||||
import { Tool } from "./tool"
|
||||
import path from "path"
|
||||
import { LSP } from "../lsp"
|
||||
import DESCRIPTION from "./lsp.txt"
|
||||
import { Instance } from "../project/instance"
|
||||
import { pathToFileURL } from "url"
|
||||
|
||||
const operations = [
|
||||
"goToDefinition",
|
||||
"findReferences",
|
||||
"hover",
|
||||
"documentSymbol",
|
||||
"workspaceSymbol",
|
||||
"goToImplementation",
|
||||
"prepareCallHierarchy",
|
||||
"incomingCalls",
|
||||
"outgoingCalls",
|
||||
] as const
|
||||
|
||||
export const LspTool = Tool.define("lsp", {
|
||||
description: DESCRIPTION,
|
||||
parameters: z.object({
|
||||
operation: z.enum(operations).describe("The LSP operation to perform"),
|
||||
filePath: z.string().describe("The absolute or relative path to the file"),
|
||||
line: z.number().int().min(1).describe("The line number (1-based, as shown in editors)"),
|
||||
character: z.number().int().min(1).describe("The character offset (1-based, as shown in editors)"),
|
||||
}),
|
||||
execute: async (args) => {
|
||||
const file = path.isAbsolute(args.filePath) ? args.filePath : path.join(Instance.directory, args.filePath)
|
||||
const uri = pathToFileURL(file).href
|
||||
const position = {
|
||||
file,
|
||||
line: args.line - 1,
|
||||
character: args.character - 1,
|
||||
}
|
||||
|
||||
const relPath = path.relative(Instance.worktree, file)
|
||||
const title = `${args.operation} ${relPath}:${args.line}:${args.character}`
|
||||
|
||||
const exists = await Bun.file(file).exists()
|
||||
if (!exists) {
|
||||
throw new Error(`File not found: ${file}`)
|
||||
}
|
||||
|
||||
const available = await LSP.hasClients(file)
|
||||
if (!available) {
|
||||
throw new Error("No LSP server available for this file type.")
|
||||
}
|
||||
|
||||
await LSP.touchFile(file, true)
|
||||
|
||||
const result: unknown[] = await (async () => {
|
||||
switch (args.operation) {
|
||||
case "goToDefinition":
|
||||
return LSP.definition(position)
|
||||
case "findReferences":
|
||||
return LSP.references(position)
|
||||
case "hover":
|
||||
return LSP.hover(position)
|
||||
case "documentSymbol":
|
||||
return LSP.documentSymbol(uri)
|
||||
case "workspaceSymbol":
|
||||
return LSP.workspaceSymbol("")
|
||||
case "goToImplementation":
|
||||
return LSP.implementation(position)
|
||||
case "prepareCallHierarchy":
|
||||
return LSP.prepareCallHierarchy(position)
|
||||
case "incomingCalls":
|
||||
return LSP.incomingCalls(position)
|
||||
case "outgoingCalls":
|
||||
return LSP.outgoingCalls(position)
|
||||
}
|
||||
})()
|
||||
|
||||
const output = (() => {
|
||||
if (result.length === 0) return `No results found for ${args.operation}`
|
||||
return JSON.stringify(result, null, 2)
|
||||
})()
|
||||
|
||||
return {
|
||||
title,
|
||||
metadata: { result },
|
||||
output,
|
||||
}
|
||||
},
|
||||
})
|
||||
19
packages/opencode/src/tool/lsp.txt
Normal file
19
packages/opencode/src/tool/lsp.txt
Normal file
@@ -0,0 +1,19 @@
|
||||
Interact with Language Server Protocol (LSP) servers to get code intelligence features.
|
||||
|
||||
Supported operations:
|
||||
- goToDefinition: Find where a symbol is defined
|
||||
- findReferences: Find all references to a symbol
|
||||
- hover: Get hover information (documentation, type info) for a symbol
|
||||
- documentSymbol: Get all symbols (functions, classes, variables) in a document
|
||||
- workspaceSymbol: Search for symbols across the entire workspace
|
||||
- goToImplementation: Find implementations of an interface or abstract method
|
||||
- prepareCallHierarchy: Get call hierarchy item at a position (functions/methods)
|
||||
- incomingCalls: Find all functions/methods that call the function at a position
|
||||
- outgoingCalls: Find all functions/methods called by the function at a position
|
||||
|
||||
All operations require:
|
||||
- filePath: The file to operate on
|
||||
- line: The line number (1-based, as shown in editors)
|
||||
- character: The character offset (1-based, as shown in editors)
|
||||
|
||||
Note: LSP servers must be configured for the file type. If no server is available, an error will be returned.
|
||||
@@ -22,6 +22,7 @@ import { WebSearchTool } from "./websearch"
|
||||
import { CodeSearchTool } from "./codesearch"
|
||||
import { Flag } from "@/flag/flag"
|
||||
import { Log } from "@/util/log"
|
||||
import { LspTool } from "./lsp"
|
||||
|
||||
export namespace ToolRegistry {
|
||||
const log = Log.create({ service: "tool.registry" })
|
||||
@@ -102,6 +103,7 @@ export namespace ToolRegistry {
|
||||
TodoReadTool,
|
||||
WebSearchTool,
|
||||
CodeSearchTool,
|
||||
...(Flag.OPENCODE_EXPERIMENTAL_LSP_TOOL ? [LspTool] : []),
|
||||
...(config.experimental?.batch_tool === true ? [BatchTool] : []),
|
||||
...custom,
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user