diff options
-rw-r--r-- | lua/core/info.lua | 239 | ||||
-rw-r--r-- | lua/interface/popup.lua | 62 | ||||
-rw-r--r-- | lua/interface/text.lua | 79 |
3 files changed, 243 insertions, 137 deletions
diff --git a/lua/core/info.lua b/lua/core/info.lua index d5bd94ce..d9b348b5 100644 --- a/lua/core/info.lua +++ b/lua/core/info.lua @@ -1,128 +1,63 @@ -local M = {} -local indent = " " - -M.banner = { - " ", - indent - .. "⠀⣿⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀ ⠀⠀⠀ ⠀⠀ ⣺⡿⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀", - indent - .. "⠀⣿⠇⠀⠀⠀⠀⠀⣤⡄⠀⠀⢠⣤⡄⠀.⣠⣤⣤⣤⡀⠀⠀⢀⣤⣤⣤⣤⡄⠀⠀⠀⣤⣄⣤⣤⣤⠀⠀ ⣿⣯ ⣿⡟⠀ ⣤⣤⠀⠀⠀⠀⣠⣤⣤⣤⣄⣤⣤", - indent - .. "⢠⣿⠀⠀⠀⠀⠀⠀⣿⠃⠀⠀⣸⣿⠁⠀⣿⣿⠉⠀⠈⣿⡇⠀⠀⠛⠋⠀⠀⢹⣿⠀⠀⠀⣿⠏⠀⠸⠿⠃⠀⣿⣿⠀⣰⡟⠀⠀⠀⠀⠀⢸⣿⠀⠀⠀⠀⣿⡟⢸⣿⡇⢀⣿", - indent - .. "⣸⡇⠀⠀⠀⠀⠀⢸⣿⠀⠀⠀⣿⡟⠀⢠⣿⡇⠀⠀⢰⣿⡇⠀⣰⣾⠟⠛⠛⣻⡇⠀⠀⢸⡿⠀⠀⠀⠀⠀⠀⢻⣿⢰⣿⠀⠀⠀⠀⠀⠀⣾⡇⠀⠀⠀⢸⣿⠇⢸⣿⠀⢸⡏", - indent - .. "⣿⣧⣤⣤⣤⡄⠀⠘⣿⣤⣤⡤⣿⠇⠀⢸⣿⠁⠀⠀⣼⣿⠀⠀⢿⣿⣤⣤⠔⣿⠃⠀⠀⣾⡇⠀⠀⠀⠀⠀⠀⢸⣿⣿⠋⠀⠀⠀⢠⣤⣤⣿⣥⣤⡄⠀⣼⣿⠀⣸⡏⠀⣿⠃", - indent - .. "⠉⠉⠉⠉⠉⠁⠀⠀⠈⠉⠉⠀⠉⠀⠀⠈⠉⠀⠀⠀⠉⠉⠀⠀⠀⠉⠉⠁⠈⠉⠀⠀⠀⠉⠀⠀⠀⠀⠀⠀⠀⠈⠉⠉⠀⠀⠀⠀⠈⠉⠉⠉⠉⠉⠁⠀⠉⠁⠀⠉⠁⠀⠉⠀", - "", +local M = { + banner = { + "", + [[ __ _ ___ ]], + [[ / / __ ______ ____ _____| | / (_)___ ___ ]], + [[ / / / / / / __ \/ __ `/ ___/ | / / / __ `__ \]], + [[ / /___/ /_/ / / / / /_/ / / | |/ / / / / / / /]], + [[/_____/\__,_/_/ /_/\__,_/_/ |___/_/_/ /_/ /_/ ]], + }, } +local fmt = string.format + local function str_list(list) - return "[ " .. table.concat(list, ", ") .. " ]" + return fmt("[ %s ]", table.concat(list, ", ")) end local function get_formatter_suggestion_msg(ft) local null_formatters = require "lsp.null-ls.formatters" local supported_formatters = null_formatters.list_available(ft) - return { - indent - .. "───────────────────────────────────────────────────────────────────", - "", - indent .. " HINT ", - "", - indent .. "* List of supported formatters: " .. str_list(supported_formatters), - indent .. "* Configured formatter needs to be installed and executable.", - indent .. "* Enable installed formatter(s) with following config in ~/.config/lvim/config.lua", - "", - indent .. " lvim.lang." .. tostring(ft) .. [[.formatters = { { exe = ']] .. table.concat( - supported_formatters, - "│" - ) .. [[' } }]], + local section = { + " HINT ", "", + fmt("* List of supported formatters: %s", str_list(supported_formatters)), } + + if not vim.tbl_isempty(supported_formatters) then + vim.list_extend(section, { + "* Configured formatter needs to be installed and executable.", + fmt("* Enable installed formatter(s) with following config in %s", USER_CONFIG_PATH), + "", + fmt(" lvim.lang.%s.formatters = { { exe = '%s' } }", ft, table.concat(supported_formatters, "│")), + }) + end + + return section end local function get_linter_suggestion_msg(ft) local null_linters = require "lsp.null-ls.linters" local supported_linters = null_linters.list_available(ft) - return { - indent - .. "───────────────────────────────────────────────────────────────────", - "", - indent .. " HINT ", - "", - indent .. "* List of supported linters: " .. str_list(supported_linters), - indent .. "* Configured linter needs to be installed and executable.", - indent .. "* Enable installed linter(s) with following config in ~/.config/lvim/config.lua", - "", - indent - .. " lvim.lang." - .. tostring(ft) - .. [[.linters = { { exe = ']] - .. table.concat(supported_linters, "│") - .. [[' } }]], + local section = { + " HINT ", "", + fmt("* List of supported linters: %s", str_list(supported_linters)), } -end ----creates an average size popup ----@param buf_lines a list of lines to print ----@param callback could be used to set syntax highlighting rules for example ----@return bufnr buffer number of the created buffer ----@return win_id window ID of the created popup -function M.create_simple_popup(buf_lines, callback) - -- runtime/lua/vim/lsp/util.lua - local bufnr = vim.api.nvim_create_buf(false, true) - local height_percentage = 0.9 - local width_percentage = 0.8 - local row_start_percentage = (1 - height_percentage) / 2 - local col_start_percentage = (1 - width_percentage) / 2 - local opts = {} - opts.relative = "editor" - opts.height = math.min(math.ceil(vim.o.lines * height_percentage), #buf_lines) - opts.row = math.ceil(vim.o.lines * row_start_percentage) - opts.col = math.floor(vim.o.columns * col_start_percentage) - opts.width = math.floor(vim.o.columns * width_percentage) - opts.style = "minimal" - opts.border = "rounded" - --[[ - opts.border = { - lvim.builtin.telescope.defaults.borderchars[5], -- "┌", - lvim.builtin.telescope.defaults.borderchars[3], -- "-", - lvim.builtin.telescope.defaults.borderchars[6], -- "┐", - lvim.builtin.telescope.defaults.borderchars[2], -- "|", - lvim.builtin.telescope.defaults.borderchars[7], -- "┘", - lvim.builtin.telescope.defaults.borderchars[3], -- "-", - lvim.builtin.telescope.defaults.borderchars[8], -- "└", - lvim.builtin.telescope.defaults.borderchars[4], -- "|", - } - --]] - - local win_id = vim.api.nvim_open_win(bufnr, true, opts) - - vim.api.nvim_win_set_buf(win_id, bufnr) - -- this needs to be window option! - vim.api.nvim_win_set_option(win_id, "number", false) - vim.cmd "setlocal nocursorcolumn" - vim.cmd "setlocal wrap" - -- set buffer options - vim.api.nvim_buf_set_option(bufnr, "filetype", "lspinfo") - vim.lsp.util.close_preview_autocmd({ "BufHidden", "BufLeave" }, win_id) - buf_lines = vim.lsp.util._trim(buf_lines, {}) - vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, buf_lines) - vim.api.nvim_buf_set_option(bufnr, "modifiable", false) - if type(callback) == "function" then - callback() + if not vim.tbl_isempty(supported_linters) then + vim.list_extend(section, { + "* Configured linter needs to be installed and executable.", + fmt("* Enable installed linter(s) with following config in %s", USER_CONFIG_PATH), + "", + fmt(" lvim.lang.%s.linters = { { exe = '%s' } }", ft, table.concat(supported_linters, "│")), + }) end - return bufnr, win_id + + return section end local function tbl_set_highlight(terms, highlight_group) - if type(terms) ~= "table" then - return - end - for _, v in pairs(terms) do vim.cmd('let m=matchadd("' .. highlight_group .. '", "' .. v .. "[ ,│']\")") end @@ -136,67 +71,90 @@ function M.toggle_popup(ft) local client_name = "" local client_id = 0 local document_formatting = false - local num_caps = 0 if client ~= nil then is_client_active = not client.is_stopped() client_enabled_caps = require("lsp").get_ls_capabilities(client.id) - num_caps = vim.tbl_count(client_enabled_caps) client_name = client.name client_id = client.id document_formatting = client.resolved_capabilities.document_formatting end - local buf_lines = {} - vim.list_extend(buf_lines, M.banner) - local header = { - indent .. "Detected filetype: " .. tostring(ft), - indent .. "Treesitter active: " .. tostring(next(vim.treesitter.highlighter.active) ~= nil), - "", + fmt("Detected filetype: %s", ft), + fmt("Treesitter active: %s", tostring(next(vim.treesitter.highlighter.active) ~= nil)), } - vim.list_extend(buf_lines, header) + local text = require "interface.text" local lsp_info = { - indent .. "Language Server Protocol (LSP) info", - indent .. "* Associated server: " .. client_name, - indent .. "* Active: " .. tostring(is_client_active) .. " (id: " .. tostring(client_id) .. ")", - indent .. "* Supports formatting: " .. tostring(document_formatting), - indent .. "* Capabilities list: " .. table.concat(vim.list_slice(client_enabled_caps, 1, num_caps / 2), ", "), - indent .. indent .. indent .. table.concat(vim.list_slice(client_enabled_caps, ((num_caps / 2) + 1)), ", "), - "", + "Language Server Protocol (LSP) info", + fmt("* Associated server: %s", client_name), + fmt("* Active: %s (id: %d)", tostring(is_client_active), client_id), + fmt("* Supports formatting: %s", tostring(document_formatting)), } - vim.list_extend(buf_lines, lsp_info) - + if not vim.tbl_isempty(client_enabled_caps) then + local caps_text = "* Capabilities list: " + local caps_text_len = caps_text:len() + local enabled_caps = text.format_table(client_enabled_caps, 3, " | ") + enabled_caps = text.shift_left(enabled_caps, caps_text_len) + enabled_caps[1] = fmt("%s%s", caps_text, enabled_caps[1]:sub(caps_text_len + 1)) + vim.list_extend(lsp_info, enabled_caps) + end local null_ls = require "lsp.null-ls" local registered_providers = null_ls.list_supported_provider_names(ft) + local registered_count = vim.tbl_count(registered_providers) local null_ls_info = { - indent .. "Formatters and linters", - indent .. "* Configured providers: " .. table.concat(registered_providers, " , ") .. " ", + "Formatters and linters", + fmt( + "* Configured providers: %s%s", + table.concat(registered_providers, " , "), + registered_count > 0 and " " or "" + ), } - vim.list_extend(buf_lines, null_ls_info) local null_formatters = require "lsp.null-ls.formatters" local missing_formatters = null_formatters.list_unsupported_names(ft) - if vim.tbl_count(missing_formatters) > 0 then - local missing_formatters_status = { - indent .. "* Missing formatters: " .. table.concat(missing_formatters, " , ") .. " ", + local missing_formatters_status = {} + if not vim.tbl_isempty(missing_formatters) then + missing_formatters_status = { + fmt("* Missing formatters: %s", table.concat(missing_formatters, " , ") .. " "), } - vim.list_extend(buf_lines, missing_formatters_status) end local null_linters = require "lsp.null-ls.linters" local missing_linters = null_linters.list_unsupported_names(ft) - if vim.tbl_count(missing_linters) > 0 then - local missing_linters_status = { - indent .. "* Missing linters: " .. table.concat(missing_linters, " , ") .. " ", + local missing_linters_status = {} + if not vim.tbl_isempty(missing_linters) then + missing_linters_status = { + fmt("* Missing linters: %s", table.concat(missing_linters, " , ") .. " "), } - vim.list_extend(buf_lines, missing_linters_status) end - vim.list_extend(buf_lines, { "" }) - - vim.list_extend(buf_lines, get_formatter_suggestion_msg(ft)) - vim.list_extend(buf_lines, get_linter_suggestion_msg(ft)) + local content_provider = function(popup) + local content = {} + + for _, section in ipairs { + M.banner, + { "" }, + { "" }, + header, + { "" }, + lsp_info, + { "" }, + null_ls_info, + missing_formatters_status, + missing_linters_status, + { "" }, + { "" }, + get_formatter_suggestion_msg(ft), + { "" }, + { "" }, + get_linter_suggestion_msg(ft), + } do + vim.list_extend(content, section) + end + + return text.align(popup, content, 0.5) + end local function set_syntax_hl() vim.cmd [[highlight LvimInfoIdentifier gui=bold]] @@ -214,6 +172,13 @@ function M.toggle_popup(ft) vim.cmd('let m=matchadd("LvimInfoIdentifier", "' .. client_name .. '")') end - return M.create_simple_popup(buf_lines, set_syntax_hl) + local Popup = require("interface.popup"):new { + win_opts = { number = false }, + buf_opts = { modifiable = false, filetype = "lspinfo" }, + } + Popup:display(content_provider) + set_syntax_hl() + + return Popup end return M diff --git a/lua/interface/popup.lua b/lua/interface/popup.lua new file mode 100644 index 00000000..b628125c --- /dev/null +++ b/lua/interface/popup.lua @@ -0,0 +1,62 @@ +local Popup = {} + +--- Create a new floating window +-- @param config The configuration passed to vim.api.nvim_open_win +-- @param win_opts The options registered with vim.api.nvim_win_set_option +-- @param buf_opts The options registered with vim.api.nvim_buf_set_option +-- @return A new popup +function Popup:new(opts) + opts = opts or {} + opts.layout = opts.layout or {} + opts.win_opts = opts.win_opts or {} + opts.buf_opts = opts.buf_opts or {} + + Popup.__index = Popup + + local editor_layout = { + height = vim.o.lines - vim.o.cmdheight - 2, -- Add margin for status and buffer line + width = vim.o.columns, + } + local popup_layout = { + relative = "editor", + height = math.floor(editor_layout.height * 0.9), + width = math.floor(editor_layout.width * 0.8), + style = "minimal", + border = "rounded", + } + popup_layout.row = math.floor((editor_layout.height - popup_layout.height) / 2) + popup_layout.col = math.floor((editor_layout.width - popup_layout.width) / 2) + + local obj = { + buffer = vim.api.nvim_create_buf(false, true), + layout = vim.tbl_deep_extend("force", popup_layout, opts.layout), + win_opts = opts.win_opts, + buf_opts = opts.buf_opts, + } + + setmetatable(obj, Popup) + + return obj +end + +--- Display the popup with the provided content +-- @param content_provider A function accepting the popup's layout and returning the content to display +function Popup:display(content_provider) + self.win_id = vim.api.nvim_open_win(self.buffer, true, self.layout) + vim.lsp.util.close_preview_autocmd({ "BufHidden", "BufLeave" }, self.win_id) + + local lines = content_provider(self.layout) + vim.api.nvim_buf_set_lines(self.bufnr, 0, -1, false, lines) + + -- window options + for key, value in pairs(self.win_opts) do + vim.api.nvim_win_set_option(self.win_id, key, value) + end + + -- buffer options + for key, value in pairs(self.buf_opts) do + vim.api.nvim_buf_set_option(self.buffer, key, value) + end +end + +return Popup diff --git a/lua/interface/text.lua b/lua/interface/text.lua new file mode 100644 index 00000000..f68cc491 --- /dev/null +++ b/lua/interface/text.lua @@ -0,0 +1,79 @@ +local M = {} + +local function max_len_line(lines) + local max_len = 0 + + for _, line in ipairs(lines) do + local line_len = line:len() + if line_len > max_len then + max_len = line_len + end + end + + return max_len +end + +--- Center align lines relatively to the parent container +-- @param container The container where lines will be displayed +-- @param lines The text to align +-- @param alignment The alignment value, range: [0-1] +function M.align(container, lines, alignment) + local max_len = max_len_line(lines) + local indent_amount = math.ceil(math.max(container.width - max_len, 0) * alignment) + return M.shift_left(lines, indent_amount) +end + +--- Shift lines by a given amount +-- @params lines The lines the shift +-- @param amount The amount of spaces to add +function M.shift_left(lines, amount) + local output = {} + local padding = string.rep(" ", amount) + + for _, line in ipairs(lines) do + table.insert(output, padding .. line) + end + + return output +end + +--- Pretty format tables +-- @param entries The table to format +-- @param col_count The number of column to span the table on +-- @param col_sep The separator between each colummn, default: " " +function M.format_table(entries, col_count, col_sep) + col_sep = col_sep or " " + + local col_rows = math.ceil(vim.tbl_count(entries) / col_count) + local cols = {} + local count = 0 + + for i, entry in ipairs(entries) do + if ((i - 1) % col_rows) == 0 then + table.insert(cols, {}) + count = count + 1 + end + table.insert(cols[count], entry) + end + + local col_max_len = {} + for _, col in ipairs(cols) do + table.insert(col_max_len, max_len_line(col)) + end + + local output = {} + for i, col in ipairs(cols) do + for j, entry in ipairs(col) do + if not output[j] then + output[j] = entry + else + local padding = string.rep(" ", col_max_len[i - 1] - cols[i - 1][j]:len()) + output[j] = output[j] .. padding .. col_sep .. entry + end + end + end + + return output +end + +return M |