Files
chatgpt.vim/lua/chatgpt_nvim/tools/lsp_robust_diagnostics.lua

178 lines
4.6 KiB
Lua

local api = vim.api
local lsp = vim.lsp
local M = {}
local function guess_filetype(path)
local ext = path:match("^.+%.([^./\\]+)$")
if not ext then return nil end
ext = ext:lower()
local map = {
lua = "lua",
go = "go",
rs = "rust",
js = "javascript",
jsx = "javascriptreact",
ts = "typescript",
tsx = "typescriptreact",
php = "php",
}
return map[ext]
end
local function create_scratch_buffer(path, content)
-- Create a unique buffer name so we never clash with an existing one
local scratch_name = string.format("chatgpt-scratch://%s#%d", path, math.random(100000, 999999))
local bufnr = api.nvim_create_buf(false, true)
if bufnr == 0 then
return nil
end
-- Assign the unique name to the buffer
api.nvim_buf_set_name(bufnr, scratch_name)
-- Convert content string to lines
local lines = {}
for line in (content.."\n"):gmatch("(.-)\n") do
table.insert(lines, line)
end
api.nvim_buf_set_lines(bufnr, 0, -1, false, lines)
-- Mark it as a scratch buffer
api.nvim_buf_set_option(bufnr, "bufhidden", "wipe")
api.nvim_buf_set_option(bufnr, "swapfile", false)
return bufnr
end
local function attach_existing_lsp_client(bufnr, filetype)
local clients = lsp.get_active_clients()
if not clients or #clients == 0 then
return nil, "No active LSP clients"
end
for _, client in ipairs(clients) do
local ft_conf = client.config.filetypes or {}
if vim.tbl_contains(ft_conf, filetype) then
if not client.attached_buffers[bufnr] then
vim.lsp.buf_attach_client(bufnr, client.id)
end
return client.id
end
end
return nil, ("No active LSP client supports filetype '%s'"):format(filetype)
end
local function send_did_open(bufnr, client_id, path, filetype)
local client = lsp.get_client_by_id(client_id)
if not client then
return "Invalid client ID."
end
local text = table.concat(api.nvim_buf_get_lines(bufnr, 0, -1, false), "\n")
-- Even though the buffer name is unique, the LSP server sees this file as if at 'path'
local uri = vim.uri_from_fname(path)
local didOpenParams = {
textDocument = {
uri = uri,
languageId = filetype,
version = 0,
text = text,
}
}
client.rpc.notify("textDocument/didOpen", didOpenParams)
return nil
end
local function send_did_change(bufnr, client_id)
local client = lsp.get_client_by_id(client_id)
if not client then return end
local text = table.concat(api.nvim_buf_get_lines(bufnr, 0, -1, false), "\n")
local uri = vim.uri_from_bufnr(bufnr)
local version = 1
client.rpc.notify("textDocument/didChange", {
textDocument = {
uri = uri,
version = version,
},
contentChanges = {
{ text = text }
}
})
end
local function wait_for_diagnostics(bufnr, timeout_ms)
local done = false
local result_diags = {}
local augrp = api.nvim_create_augroup("chatgpt_lsp_diag_"..bufnr, { clear = true })
api.nvim_create_autocmd("DiagnosticChanged", {
group = augrp,
callback = function(args)
if args.buf == bufnr then
local diags = vim.diagnostic.get(bufnr)
result_diags = diags
done = true
end
end
})
local waited = 0
local interval = 50
while not done and waited < timeout_ms do
vim.cmd(("sleep %d m"):format(interval))
waited = waited + interval
end
pcall(api.nvim_del_augroup_by_id, augrp)
return result_diags
end
local function diagnostics_to_string(diags)
if #diags == 0 then
return "No LSP diagnostics reported."
end
local lines = { "--- LSP Diagnostics ---" }
for _, d in ipairs(diags) do
local sev = vim.diagnostic.severity[d.severity] or d.severity
lines[#lines+1] = string.format("Line %d [%s]: %s", d.lnum + 1, sev, d.message)
end
return table.concat(lines, "\n")
end
function M.lsp_check_file_content(path, new_content, timeout_ms)
local filetype = guess_filetype(path) or "plaintext"
local bufnr = create_scratch_buffer(path, new_content)
if not bufnr then
return "(LSP) Could not create scratch buffer."
end
local client_id, err = attach_existing_lsp_client(bufnr, filetype)
if not client_id then
api.nvim_buf_delete(bufnr, { force = true })
return "(LSP) " .. (err or "No suitable LSP client.")
end
local err2 = send_did_open(bufnr, client_id, path, filetype)
if err2 then
api.nvim_buf_delete(bufnr, { force = true })
return "(LSP) " .. err2
end
-- Optionally do a didChange
send_did_change(bufnr, client_id)
local diags = wait_for_diagnostics(bufnr, timeout_ms or 2000)
local diag_str = diagnostics_to_string(diags)
api.nvim_buf_delete(bufnr, { force = true })
return diag_str
end
return M