feat: lsp tools (#1588)
This commit is contained in:
@@ -1631,6 +1631,57 @@ M._tools = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name = "read_definitions",
|
||||||
|
description = "Retrieves the complete source code definitions of any symbol (function, type, constant, etc.) from your codebase.",
|
||||||
|
param = {
|
||||||
|
type = "table",
|
||||||
|
fields = {
|
||||||
|
{
|
||||||
|
name = "symbol_name",
|
||||||
|
description = "The name of the symbol to retrieve the definition for",
|
||||||
|
type = "string",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name = "show_line_numbers",
|
||||||
|
description = "Whether to show line numbers in the definitions",
|
||||||
|
type = "boolean",
|
||||||
|
default = false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
returns = {
|
||||||
|
{
|
||||||
|
name = "definitions",
|
||||||
|
description = "The source code definitions of the symbol",
|
||||||
|
type = "string[]",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name = "error",
|
||||||
|
description = "Error message if the definition retrieval failed",
|
||||||
|
type = "string",
|
||||||
|
optional = true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
func = function(input_json, on_log, on_complete)
|
||||||
|
local symbol_name = input_json.symbol_name
|
||||||
|
local show_line_numbers = input_json.show_line_numbers
|
||||||
|
if on_log then on_log("symbol_name: " .. vim.inspect(symbol_name)) end
|
||||||
|
if on_log then on_log("show_line_numbers: " .. vim.inspect(show_line_numbers)) end
|
||||||
|
if not symbol_name then return nil, "No symbol name provided" end
|
||||||
|
local sidebar = require("avante").get()
|
||||||
|
if not sidebar then return nil, "No sidebar" end
|
||||||
|
local bufnr = sidebar.code.bufnr
|
||||||
|
if not bufnr then return nil, "No bufnr" end
|
||||||
|
if not vim.api.nvim_buf_is_valid(bufnr) then return nil, "Invalid bufnr" end
|
||||||
|
if on_log then on_log("bufnr: " .. vim.inspect(bufnr)) end
|
||||||
|
Utils.lsp.read_definitions(bufnr, symbol_name, show_line_numbers, function(definitions, error)
|
||||||
|
local encoded_defs = vim.json.encode(definitions)
|
||||||
|
on_complete(encoded_defs, error)
|
||||||
|
end)
|
||||||
|
return nil, nil
|
||||||
|
end,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
---@param tools AvanteLLMTool[]
|
---@param tools AvanteLLMTool[]
|
||||||
|
|||||||
@@ -401,3 +401,7 @@ vim.g.avante_login = vim.g.avante_login
|
|||||||
---@field handler_opts AvanteHandlerOptions
|
---@field handler_opts AvanteHandlerOptions
|
||||||
---@field on_response_headers? fun(headers: table<string, string>): nil
|
---@field on_response_headers? fun(headers: table<string, string>): nil
|
||||||
---
|
---
|
||||||
|
---@class avante.lsp.Definition
|
||||||
|
---@field content string
|
||||||
|
---@field uri string
|
||||||
|
---
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ local lsp = vim.lsp
|
|||||||
---@field file avante.utils.file
|
---@field file avante.utils.file
|
||||||
---@field history avante.utils.history
|
---@field history avante.utils.history
|
||||||
---@field environment avante.utils.environment
|
---@field environment avante.utils.environment
|
||||||
|
---@field lsp avante.utils.lsp
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
setmetatable(M, {
|
setmetatable(M, {
|
||||||
@@ -313,18 +314,6 @@ function M.get_hl(name)
|
|||||||
return api.nvim_get_hl(0, { name = name })
|
return api.nvim_get_hl(0, { name = name })
|
||||||
end
|
end
|
||||||
|
|
||||||
M.lsp = {}
|
|
||||||
|
|
||||||
---@alias vim.lsp.Client.filter {id?: number, bufnr?: number, name?: string, method?: string, filter?:fun(client: vim.lsp.Client):boolean}
|
|
||||||
|
|
||||||
---@param opts? vim.lsp.Client.filter
|
|
||||||
---@return vim.lsp.Client[]
|
|
||||||
function M.lsp.get_clients(opts)
|
|
||||||
---@type vim.lsp.Client[]
|
|
||||||
local ret = vim.lsp.get_clients(opts)
|
|
||||||
return (opts and opts.filter) and vim.tbl_filter(opts.filter, ret) or ret
|
|
||||||
end
|
|
||||||
|
|
||||||
--- vendor from lazy.nvim for early access and override
|
--- vendor from lazy.nvim for early access and override
|
||||||
|
|
||||||
---@param path string
|
---@param path string
|
||||||
|
|||||||
112
lua/avante/utils/lsp.lua
Normal file
112
lua/avante/utils/lsp.lua
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
---@class avante.utils.lsp
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
---@alias vim.lsp.Client.filter {id?: number, bufnr?: number, name?: string, method?: string, filter?:fun(client: vim.lsp.Client):boolean}
|
||||||
|
|
||||||
|
---@param opts? vim.lsp.Client.filter
|
||||||
|
---@return vim.lsp.Client[]
|
||||||
|
function M.get_clients(opts)
|
||||||
|
---@type vim.lsp.Client[]
|
||||||
|
local ret = vim.lsp.get_clients(opts)
|
||||||
|
return (opts and opts.filter) and vim.tbl_filter(opts.filter, ret) or ret
|
||||||
|
end
|
||||||
|
|
||||||
|
--- return function or variable or class
|
||||||
|
local function get_ts_node_parent(node)
|
||||||
|
if not node then return nil end
|
||||||
|
local type = node:type()
|
||||||
|
if
|
||||||
|
type:match("function")
|
||||||
|
or type:match("method")
|
||||||
|
or type:match("variable")
|
||||||
|
or type:match("class")
|
||||||
|
or type:match("type")
|
||||||
|
or type:match("parameter")
|
||||||
|
or type:match("field")
|
||||||
|
or type:match("property")
|
||||||
|
or type:match("enum")
|
||||||
|
or type:match("assignment")
|
||||||
|
or type:match("struct")
|
||||||
|
or type:match("declaration")
|
||||||
|
then
|
||||||
|
return node
|
||||||
|
end
|
||||||
|
return get_ts_node_parent(node:parent())
|
||||||
|
end
|
||||||
|
|
||||||
|
local function get_full_definition(location)
|
||||||
|
local uri = location.uri
|
||||||
|
local filepath = uri:gsub("^file://", "")
|
||||||
|
local full_lines = vim.fn.readfile(filepath)
|
||||||
|
local buf = vim.api.nvim_create_buf(false, true)
|
||||||
|
vim.api.nvim_buf_set_lines(buf, 0, -1, false, full_lines)
|
||||||
|
local filetype = vim.filetype.match({ filename = filepath, buf = buf }) or ""
|
||||||
|
|
||||||
|
--- use tree-sitter to get the full definition
|
||||||
|
local parser = require("nvim-treesitter.parsers").get_parser(buf, filetype)
|
||||||
|
local tree = parser:parse()[1]
|
||||||
|
local root = tree:root()
|
||||||
|
local node = root:named_descendant_for_range(
|
||||||
|
location.range.start.line,
|
||||||
|
location.range.start.character,
|
||||||
|
location.range.start.line,
|
||||||
|
location.range.start.character
|
||||||
|
)
|
||||||
|
if not node then
|
||||||
|
vim.api.nvim_buf_delete(buf, { force = true })
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
local parent = get_ts_node_parent(node)
|
||||||
|
if not parent then parent = node end
|
||||||
|
local text = vim.treesitter.get_node_text(parent, buf)
|
||||||
|
vim.api.nvim_buf_delete(buf, { force = true })
|
||||||
|
return vim.split(text, "\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param bufnr number
|
||||||
|
---@param symbol_name string
|
||||||
|
---@param show_line_numbers boolean
|
||||||
|
---@param on_complete fun(definitions: avante.lsp.Definition[] | nil, error: string | nil)
|
||||||
|
function M.read_definitions(bufnr, symbol_name, show_line_numbers, on_complete)
|
||||||
|
local clients = vim.lsp.get_clients({ bufnr = bufnr })
|
||||||
|
if #clients == 0 then
|
||||||
|
on_complete(nil, "No LSP client found")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local params = { query = symbol_name }
|
||||||
|
vim.lsp.buf_request_all(bufnr, "workspace/symbol", params, function(results)
|
||||||
|
if not results or #results == 0 then
|
||||||
|
on_complete(nil, "No results")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
for _, result in ipairs(results) do
|
||||||
|
if result.error then
|
||||||
|
on_complete(nil, result.error.message)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local definitions = vim.tbl_filter(function(d) return d.name == symbol_name end, result.result)
|
||||||
|
if #definitions == 0 then
|
||||||
|
on_complete(nil, "No definition found")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
---@type avante.lsp.Definition[]
|
||||||
|
local res = {}
|
||||||
|
for _, definition in ipairs(definitions) do
|
||||||
|
local lines = get_full_definition(definition.location)
|
||||||
|
if show_line_numbers then
|
||||||
|
local start_line = definition.location.range.start.line
|
||||||
|
local new_lines = {}
|
||||||
|
for i, line_ in ipairs(lines) do
|
||||||
|
table.insert(new_lines, tostring(start_line + i) .. ": " .. line_)
|
||||||
|
end
|
||||||
|
lines = new_lines
|
||||||
|
end
|
||||||
|
local uri = definition.location.uri
|
||||||
|
table.insert(res, { content = table.concat(lines, "\n"), uri = uri })
|
||||||
|
end
|
||||||
|
on_complete(res, nil)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
Reference in New Issue
Block a user