diff options
Diffstat (limited to 'runtime/lua/vim/pack/_lsp.lua')
| -rw-r--r-- | runtime/lua/vim/pack/_lsp.lua | 168 |
1 files changed, 168 insertions, 0 deletions
diff --git a/runtime/lua/vim/pack/_lsp.lua b/runtime/lua/vim/pack/_lsp.lua new file mode 100644 index 0000000000..52dff730c2 --- /dev/null +++ b/runtime/lua/vim/pack/_lsp.lua @@ -0,0 +1,168 @@ +local M = {} + +local capabilities = { + codeActionProvider = true, + documentSymbolProvider = true, + hoverProvider = true, +} +--- @type table<string,function> +local methods = {} + +--- @param callback function +function methods.initialize(_, callback) + return callback(nil, { capabilities = capabilities }) +end + +--- @param callback function +function methods.shutdown(_, callback) + return callback(nil, nil) +end + +local get_confirm_bufnr = function(uri) + return tonumber(uri:match('^nvim%-pack://(%d+)/confirm%-update$')) +end + +--- @param params { textDocument: { uri: string } } +--- @param callback function +methods['textDocument/documentSymbol'] = function(params, callback) + local bufnr = get_confirm_bufnr(params.textDocument.uri) + if bufnr == nil then + return callback(nil, {}) + end + + --- @alias vim.pack.lsp.Position { line: integer, character: integer } + --- @alias vim.pack.lsp.Range { start: vim.pack.lsp.Position, end: vim.pack.lsp.Position } + --- @alias vim.pack.lsp.Symbol { + --- name: string, + --- kind: number, + --- range: vim.pack.lsp.Range, + --- selectionRange: vim.pack.lsp.Range, + --- children: vim.pack.lsp.Symbol[]?, + --- } + + --- @return vim.pack.lsp.Symbol? + local new_symbol = function(name, start_line, end_line, kind) + if name == nil then + return nil + end + local range = { + start = { line = start_line, character = 0 }, + ['end'] = { line = end_line, character = 0 }, + } + return { name = name, kind = kind, range = range, selectionRange = range } + end + + local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false) + + --- @return vim.pack.lsp.Symbol[] + local parse_headers = function(pattern, start_line, end_line, kind) + local res, cur_match, cur_start = {}, nil, nil + for i = start_line, end_line do + local m = lines[i + 1]:match(pattern) + if m ~= nil and m ~= cur_match then + table.insert(res, new_symbol(cur_match, cur_start, i, kind)) + cur_match, cur_start = m, i + end + end + table.insert(res, new_symbol(cur_match, cur_start, end_line, kind)) + return res + end + + local group_kind = vim.lsp.protocol.SymbolKind.Namespace + local symbols = parse_headers('^# (%S+)', 0, #lines - 1, group_kind) + + local plug_kind = vim.lsp.protocol.SymbolKind.Module + for _, group in ipairs(symbols) do + local start_line, end_line = group.range.start.line, group.range['end'].line + group.children = parse_headers('^## (.+)$', start_line, end_line, plug_kind) + end + + return callback(nil, symbols) +end + +--- @param callback function +methods['textDocument/codeAction'] = function(_, callback) + -- TODO(echasnovski) + -- Suggested actions for "plugin under cursor": + -- - Delete plugin from disk. + -- - Update only this plugin. + -- - Exclude this plugin from update. + return callback(_, {}) +end + +--- @param params { textDocument: { uri: string }, position: { line: integer, character: integer } } +--- @param callback function +methods['textDocument/hover'] = function(params, callback) + local bufnr = get_confirm_bufnr(params.textDocument.uri) + if bufnr == nil then + return + end + + local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false) + local lnum = params.position.line + 1 + local commit = lines[lnum]:match('^[<>] (%x+) │') or lines[lnum]:match('^State.*:%s+(%x+)') + local tag = lines[lnum]:match('^• (.+)$') + if commit == nil and tag == nil then + return + end + + local path, path_lnum = nil, lnum - 1 + while path == nil and path_lnum >= 1 do + path = lines[path_lnum]:match('^Path:%s+(.+)$') + path_lnum = path_lnum - 1 + end + if path == nil then + return + end + + local cmd = { 'git', 'show', '--no-color', commit or tag } + --- @param sys_out vim.SystemCompleted + local on_exit = function(sys_out) + local markdown = '```diff\n' .. sys_out.stdout .. '\n```' + local res = { contents = { kind = vim.lsp.protocol.MarkupKind.Markdown, value = markdown } } + callback(nil, res) + end + vim.system(cmd, { cwd = path }, vim.schedule_wrap(on_exit)) +end + +local dispatchers = {} + +-- TODO: Simplify after `vim.lsp.server` is a thing +-- https://github.com/neovim/neovim/pull/24338 +local cmd = function(disp) + -- Store dispatchers to use for showing progress notifications + dispatchers = disp + local res, closing, request_id = {}, false, 0 + + function res.request(method, params, callback) + local method_impl = methods[method] + if method_impl ~= nil then + method_impl(params, callback) + end + request_id = request_id + 1 + return true, request_id + end + + function res.notify(method, _) + if method == 'exit' then + dispatchers.on_exit(0, 15) + end + return false + end + + function res.is_closing() + return closing + end + + function res.terminate() + closing = true + end + + return res +end + +M.client_id = assert( + vim.lsp.start({ cmd = cmd, name = 'vim.pack', root_dir = vim.uv.cwd() }, { attach = false }) +) + +return M |