diff options
Diffstat (limited to 'lua')
| -rw-r--r-- | lua/lspconfig.lua | 2 | ||||
| -rw-r--r-- | lua/lspconfig/health.lua (renamed from lua/lspconfig/ui/lspinfo.lua) | 237 | ||||
| -rw-r--r-- | lua/lspconfig/ui/windows.lua | 124 |
3 files changed, 80 insertions, 283 deletions
diff --git a/lua/lspconfig.lua b/lua/lspconfig.lua index 9c1ef7e3..3382259f 100644 --- a/lua/lspconfig.lua +++ b/lua/lspconfig.lua @@ -9,7 +9,7 @@ local M = { ---@class Alias ---@field to string The new name of the server ---@field version string The version that the alias will be removed in ----@field inconfig? boolean need shown in lspinfo +---@field inconfig? boolean should display in healthcheck (`:checkhealth lspconfig`) local aliases = { ['fennel-ls'] = { to = 'fennel_ls', diff --git a/lua/lspconfig/ui/lspinfo.lua b/lua/lspconfig/health.lua index ac8e6c2b..24d3444f 100644 --- a/lua/lspconfig/ui/lspinfo.lua +++ b/lua/lspconfig/health.lua @@ -1,12 +1,14 @@ +local M = {} +local health = require('vim.health') + local api, fn = vim.api, vim.fn -local windows = require 'lspconfig.ui.windows' local util = require 'lspconfig.util' local error_messages = { - cmd_not_found = 'Unable to find executable. Please check your path and ensure the server is installed', - no_filetype_defined = 'No filetypes defined, Please define filetypes in setup()', + cmd_not_found = 'Unable to find executable. Check your $PATH and ensure the server is installed.', + no_filetype_defined = 'No filetypes defined. Define filetypes in setup().', root_dir_not_found = 'Not found.', - async_root_dir_function = 'Asynchronous root_dir functions are not supported in :LspInfo', + async_root_dir_function = 'Asynchronous root_dir functions are not supported by `:checkhealth lspconfig`', } local helptags = { @@ -22,12 +24,6 @@ local function trim_blankspace(cmd) return trimmed_cmd end -local function indent_lines(lines, offset) - return vim.tbl_map(function(val) - return offset .. val - end, lines) -end - local function remove_newlines(cmd) cmd = trim_blankspace(cmd) cmd = table.concat(cmd, ' ') @@ -78,7 +74,7 @@ local function make_config_info(config, bufnr) end) coroutine.resume(co) if root_dir then - config_info.root_dir = root_dir + config_info.root_dir = vim.fn.fnamemodify(root_dir, ':~') elseif coroutine.status(co) == 'suspended' then config_info.root_dir = error_messages.async_root_dir_function else @@ -115,9 +111,8 @@ local function make_config_info(config, bufnr) }, info_lines) end - vim.list_extend(lines, indent_lines(info_lines, '\t')) - - return lines + vim.list_extend(lines, info_lines) + return table.concat(lines, '\n') end ---@param client vim.lsp.Client @@ -128,16 +123,12 @@ local function make_client_info(client, fname) client_info.cmd = cmd_type[type(client.config.cmd)](client.config) local workspace_folders = fn.has 'nvim-0.9' == 1 and client.workspace_folders or client.workspaceFolders local uv = vim.uv - local is_windows = uv.os_uname().version:match 'Windows' - fname = uv.fs_realpath(fname) or fn.fnamemodify(fn.resolve(fname), ':p') - if is_windows then - fname:gsub('%/', '%\\') - end + fname = vim.fs.normalize(uv.fs_realpath(fname) or fn.fnamemodify(fn.resolve(fname), ':p')) if workspace_folders then for _, schema in ipairs(workspace_folders) do local matched = true - local root_dir = uv.fs_realpath(schema.name) + local root_dir = vim.fn.fnamemodify(uv.fs_realpath(schema.name), ':~') if root_dir == nil or fname:sub(1, root_dir:len()) ~= root_dir then matched = false end @@ -157,7 +148,6 @@ local function make_client_info(client, fname) client_info.attached_buffers_list = table.concat(vim.lsp.get_buffers_by_client_id(client.id), ', ') local lines = { - '', 'Client: ' .. client.name .. ' (id: ' @@ -179,30 +169,30 @@ local function make_client_info(client, fname) info_lines = vim.list_extend(info_lines, server_specific_info) end - vim.list_extend(lines, indent_lines(info_lines, '\t')) + vim.list_extend(lines, info_lines) - return lines + return table.concat(lines, '\n') end -return function() - -- These options need to be cached before switching to the floating - -- buffer. - local original_bufnr = api.nvim_get_current_buf() - local buf_clients = util.get_lsp_clients { bufnr = original_bufnr } - local clients = util.get_lsp_clients() - local buffer_filetype = vim.bo.filetype - local fname = api.nvim_buf_get_name(original_bufnr) - - windows.default_options.wrap = true - windows.default_options.breakindent = true - windows.default_options.breakindentopt = 'shift:25' - windows.default_options.showbreak = 'NONE' +local function check_lspconfig(bufnr) + bufnr = (bufnr and bufnr ~= -1) and bufnr or nil - local win_info = windows.percentage_range_window(0.8, 0.7) - local bufnr, win_id = win_info.bufnr, win_info.win_id - vim.bo.bufhidden = 'wipe' + health.start('LSP configs active in this session (globally)') + health.info('Configured servers: ' .. table.concat(util.available_servers(), ', ')) + local deprecated_servers = {} + for server_name, deprecate in pairs(require('lspconfig').server_aliases()) do + table.insert(deprecated_servers, ('%s -> %s'):format(server_name, deprecate.to)) + end + if #deprecated_servers == 0 then + health.ok('Deprecated servers: (none)') + else + health.warn('Deprecated servers: ' .. table.concat(deprecated_servers, ', ')) + end - local buf_lines = {} + local buf_clients = not bufnr and {} or vim.lsp.get_clients { bufnr = bufnr } + local clients = vim.lsp.get_clients() + local buffer_filetype = bufnr and vim.fn.getbufvar(bufnr, '&filetype') or '(invalid buffer)' + local fname = bufnr and api.nvim_buf_get_name(bufnr) or '(invalid buffer)' local buf_client_ids = {} for _, client in ipairs(buf_clients) do @@ -216,91 +206,29 @@ return function() end end - -- insert the tips at the top of window - buf_lines[#buf_lines + 1] = 'Press q or <Esc> to close this window. Press <Tab> to view server doc.' - - local header = { - '', - 'Language client log: ' .. (vim.lsp.get_log_path()), - 'Detected filetype: ' .. buffer_filetype, - } - vim.list_extend(buf_lines, header) - - local buffer_clients_header = { - '', - tostring(#vim.tbl_keys(buf_clients)) .. ' client(s) attached to this buffer: ', - } - - vim.list_extend(buf_lines, buffer_clients_header) + health.start(('LSP configs active in this buffer (id=%s)'):format(bufnr or '(invalid buffer)')) + health.info('Language client log: ' .. (vim.fn.fnamemodify(vim.lsp.get_log_path(), ':~'))) + health.info(('Detected filetype: `%s`'):format(buffer_filetype)) + health.info(('%d client(s) attached to this buffer'):format(#vim.tbl_keys(buf_clients))) for _, client in ipairs(buf_clients) do - local client_info = make_client_info(client, fname) - vim.list_extend(buf_lines, client_info) + health.info(make_client_info(client, fname)) end - local other_active_section_header = { - '', - tostring(#other_active_clients) .. ' active client(s) not attached to this buffer: ', - } if not vim.tbl_isempty(other_active_clients) then - vim.list_extend(buf_lines, other_active_section_header) - end - for _, client in ipairs(other_active_clients) do - local client_info = make_client_info(client, fname) - vim.list_extend(buf_lines, client_info) + health.info(('%s active client(s) not attached to this buffer:'):format(#other_active_clients)) + for _, client in ipairs(other_active_clients) do + health.info(make_client_info(client, fname)) + end end - local other_matching_configs_header = { - '', - 'Other clients that match the filetype: ' .. buffer_filetype, - '', - } - - local other_matching_configs = util.get_other_matching_providers(buffer_filetype) - + local other_matching_configs = not bufnr and {} or util.get_other_matching_providers(buffer_filetype) if not vim.tbl_isempty(other_matching_configs) then - vim.list_extend(buf_lines, other_matching_configs_header) + health.info(('Other clients that match the "%s" filetype: '):format(buffer_filetype)) for _, config in ipairs(other_matching_configs) do - vim.list_extend(buf_lines, make_config_info(config, original_bufnr)) + health.info(make_config_info(config, bufnr)) end end - local matching_config_header = { - '', - 'Configured servers list: ' .. table.concat(util.available_servers(), ', '), - } - local deprecated_servers = {} - vim.list_extend(buf_lines, matching_config_header) - for server_name, deprecate in pairs(require('lspconfig').server_aliases()) do - table.insert(deprecated_servers, ('%s -> %s'):format(server_name, deprecate.to)) - end - if #deprecated_servers > 0 then - vim.list_extend(buf_lines, { '', 'Deprecated servers: ' .. table.concat(deprecated_servers, ' ') }) - end - - local fmt_buf_lines = indent_lines(buf_lines, ' ') - - api.nvim_buf_set_lines(bufnr, 0, -1, true, fmt_buf_lines) - vim.bo.modifiable = false - vim.bo.filetype = 'lspinfo' - - local augroup = api.nvim_create_augroup('lspinfo', { clear = false }) - - local function close() - api.nvim_clear_autocmds { group = augroup, buffer = bufnr } - if api.nvim_win_is_valid(win_id) then - api.nvim_win_close(win_id, true) - end - end - - vim.keymap.set('n', '<ESC>', close, { buffer = bufnr, nowait = true }) - vim.keymap.set('n', 'q', close, { buffer = bufnr, nowait = true }) - api.nvim_create_autocmd({ 'BufDelete', 'BufHidden' }, { - once = true, - buffer = bufnr, - callback = close, - group = augroup, - }) - vim.fn.matchadd( 'Error', error_messages.no_filetype_defined @@ -311,65 +239,52 @@ return function() .. error_messages.root_dir_not_found ) + -- TODO(justimk): enhance :checkhealth's highlighting instead of doing this only for lspconfig. vim.cmd [[ syn keyword String true syn keyword Error false - syn match LspInfoFiletypeList /\<filetypes\?:\s*\zs.*\ze/ contains=LspInfoFiletype - syn match LspInfoFiletype /\k\+/ contained - syn match LspInfoTitle /^\s*\%(Client\|Config\):\s*\zs\S\+\ze/ - syn match LspInfoListList /^\s*Configured servers list:\s*\zs.*\ze/ contains=LspInfoList - syn match LspInfoList /\S\+/ contained ]] - api.nvim_buf_add_highlight(bufnr, 0, 'LspInfoTip', 0, 0, -1) - - local function show_doc() - local lines = {} - local function append_lines(config) - if not config then - return - end - local desc = vim.tbl_get(config, 'config_def', 'docs', 'description') - if desc then - lines[#lines + 1] = string.format('# %s', config.name) - lines[#lines + 1] = '' - vim.list_extend(lines, vim.split(desc, '\n')) - lines[#lines + 1] = '' - end - end + return buf_clients, other_matching_configs +end - lines[#lines + 1] = 'Press <Tab> to close server info.' - lines[#lines + 1] = '' +local function check_lspdocs(buf_clients, other_matching_configs) + health.start('Docs for active configs:') - for _, client in ipairs(buf_clients) do - local config = require('lspconfig.configs')[client.name] - append_lines(config) + local lines = {} + local function append_lines(config) + if not config then + return end - - for _, config in ipairs(other_matching_configs) do - append_lines(config) + local desc = vim.tbl_get(config, 'config_def', 'docs', 'description') + if desc then + lines[#lines + 1] = string.format('%s docs: >markdown', config.name) + lines[#lines + 1] = '' + vim.list_extend(lines, vim.split(desc, '\n')) + lines[#lines + 1] = '' end + end - local info = windows.percentage_range_window(0.8, 0.7) - lines = indent_lines(lines, ' ') - api.nvim_buf_set_lines(info.bufnr, 0, -1, false, lines) - api.nvim_buf_add_highlight(info.bufnr, 0, 'LspInfoTip', 0, 0, -1) + for _, client in ipairs(buf_clients) do + local config = require('lspconfig.configs')[client.name] + append_lines(config) + end - vim.bo[info.bufnr].filetype = 'markdown' - vim.bo[info.bufnr].syntax = 'on' - vim.wo[info.win_id].concealcursor = 'niv' - vim.wo[info.win_id].conceallevel = 2 - vim.wo[info.win_id].breakindent = false - vim.wo[info.win_id].breakindentopt = '' + for _, config in ipairs(other_matching_configs) do + append_lines(config) + end - local function close_doc_win() - if api.nvim_win_is_valid(info.win_id) then - api.nvim_win_close(info.win_id, true) - end - end + health.info(table.concat(lines, '\n')) +end - vim.keymap.set('n', '<TAB>', close_doc_win, { buffer = info.bufnr }) - end +function M.check() + -- XXX: :checkhealth switches to its buffer before invoking the healthcheck(s). + local orig_bufnr = vim.fn.bufnr('#') + local buf_clients, other_matching_configs = check_lspconfig(orig_bufnr) + check_lspdocs(buf_clients, other_matching_configs) - vim.keymap.set('n', '<TAB>', show_doc, { buffer = true, nowait = true }) + -- XXX: create "q" mapping until :checkhealth has this feature in Nvim stable. + vim.cmd [[nnoremap <buffer> q <c-w>q]] end + +return M diff --git a/lua/lspconfig/ui/windows.lua b/lua/lspconfig/ui/windows.lua index dc02c9ef..616d30cd 100644 --- a/lua/lspconfig/ui/windows.lua +++ b/lua/lspconfig/ui/windows.lua @@ -1,129 +1,11 @@ --- The following is extracted and modified from plenary.vnim by --- TJ Devries. It is not a stable API, and is expected to change --- -local api = vim.api - -local function apply_defaults(original, defaults) - if original == nil then - original = {} - end - - original = vim.deepcopy(original) - - for k, v in pairs(defaults) do - if original[k] == nil then - original[k] = v - end - end - - return original -end - local win_float = {} win_float.default_options = {} -function win_float.default_opts(options) - options = apply_defaults(options, { percentage = 0.9 }) - - local width = math.floor(vim.o.columns * options.percentage) - local height = math.floor(vim.o.lines * options.percentage) - - local top = math.floor(((vim.o.lines - height) / 2)) - local left = math.floor((vim.o.columns - width) / 2) - - local opts = { - relative = 'editor', - row = top, - col = left, - width = width, - height = height, - style = 'minimal', - border = { - { ' ', 'NormalFloat' }, - { ' ', 'NormalFloat' }, - { ' ', 'NormalFloat' }, - { ' ', 'NormalFloat' }, - { ' ', 'NormalFloat' }, - { ' ', 'NormalFloat' }, - { ' ', 'NormalFloat' }, - { ' ', 'NormalFloat' }, - }, - } - - opts.border = options.border and options.border - - return opts +function win_float.default_opts() + return {} end ---- Create window that takes up certain percentags of the current screen. ---- ---- Works regardless of current buffers, tabs, splits, etc. ---@param col_range number | Table: --- If number, then center the window taking up this percentage of the screen. --- If table, first index should be start, second_index should be end ---@param row_range number | Table: --- If number, then center the window taking up this percentage of the screen. --- If table, first index should be start, second_index should be end -function win_float.percentage_range_window(col_range, row_range, options) - options = apply_defaults(options, win_float.default_options) - - local win_opts = win_float.default_opts(options) - win_opts.relative = 'editor' - - local height_percentage, row_start_percentage - if type(row_range) == 'number' then - assert(row_range <= 1) - assert(row_range > 0) - height_percentage = row_range - row_start_percentage = (1 - height_percentage) / 3 - elseif type(row_range) == 'table' then - height_percentage = row_range[2] - row_range[1] - row_start_percentage = row_range[1] - else - error(string.format("Invalid type for 'row_range': %p", row_range)) - end - - win_opts.height = math.ceil(vim.o.lines * height_percentage) - win_opts.row = math.ceil(vim.o.lines * row_start_percentage) - win_opts.border = options.border or 'none' - - local width_percentage, col_start_percentage - if type(col_range) == 'number' then - assert(col_range <= 1) - assert(col_range > 0) - width_percentage = col_range - col_start_percentage = (1 - width_percentage) / 2 - elseif type(col_range) == 'table' then - width_percentage = col_range[2] - col_range[1] - col_start_percentage = col_range[1] - else - error(string.format("Invalid type for 'col_range': %p", col_range)) - end - - win_opts.col = math.floor(vim.o.columns * col_start_percentage) - win_opts.width = math.floor(vim.o.columns * width_percentage) - - local bufnr = options.bufnr or api.nvim_create_buf(false, true) - local win_id = api.nvim_open_win(bufnr, true, win_opts) - api.nvim_win_set_option(win_id, 'winhl', 'FloatBorder:LspInfoBorder') - - for k, v in pairs(win_float.default_options) do - if k ~= 'border' then - vim.opt_local[k] = v - end - end - - api.nvim_win_set_buf(win_id, bufnr) - - api.nvim_win_set_option(win_id, 'cursorcolumn', false) - api.nvim_buf_set_option(bufnr, 'tabstop', 2) - api.nvim_buf_set_option(bufnr, 'shiftwidth', 2) - - return { - bufnr = bufnr, - win_id = win_id, - } -end +function win_float.percentage_range_window() end return win_float |
