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 | 
