Browse Source

feat(modules): add new built-in plugin \(`info`\), show an useful informational dashboard

- Add `:DoomInfo` command

  - Add `<leader>di` keybind

  - Exclude `DoomInfo` filetype in blankline plugin

  - Update docs
my-config
NTBBloodbath 3 years ago
parent
commit
25e9f20e71
No known key found for this signature in database GPG Key ID: 18D6730BC846AAC5
  1. 4
      doc/doom_nvim.norg
  2. 3
      lua/doom/core/settings/init.lua
  3. 9
      lua/doom/extras/keybindings/leader.lua
  4. 532
      lua/doom/modules/built-in/info/init.lua
  5. 2
      lua/doom/modules/config/doom-blankline.lua
  6. 1
      lua/doom/modules/config/doom-whichkey.lua

4
doc/doom_nvim.norg

@ -258,6 +258,8 @@
│DoomConfigsReload │ Reload user-defined keybindings, etc │
├──────────────────┼────────────────────────────────────────────┤
│DoomReport │ Create a Doom Nvim markdown crash report │
├──────────────────┼────────────────────────────────────────────┤
│DoomInfo │ Open Doom Nivm information dashboard │
└──────────────────┴────────────────────────────────────────────┘
Doom Nvim also uses [which-key.nvim](https://github.com/folke/which-key.nvim) as a commands manager, inspired by emacs' guide-key.
@ -353,6 +355,8 @@
├─────────────────────┼────────────────────────────────────────────────────┤
│ d - d │ Open Doom Nvim documentation │
├─────────────────────┼────────────────────────────────────────────────────┤
│ d - i │ Open Doom Nivm information dashboard │
├─────────────────────┼────────────────────────────────────────────────────┤
│ d - R │ Create crash reports, useful for debugging issues │
├─────────────────────┼────────────────────────────────────────────────────┤
│ d - r │ Rollback Doom Nvim version │

3
lua/doom/core/settings/init.lua

@ -160,6 +160,9 @@ M.doom_commands = function()
-- Set a custom command to create a crash report
-- can be called by using :DoomReport
vim.cmd('command! DoomReport lua require("doom.core.functions").create_report()')
-- Set a custom command to display an information dashboard
-- can be called by using :DoomInfo
vim.cmd('command! DoomInfo lua require("doom.modules.built-in.info").toggle()')
end
-- Custom Doom Nvim options

9
lua/doom/extras/keybindings/leader.lua

@ -220,6 +220,15 @@ utils.map(
"reload_user_settings",
"Reload user custom settings"
)
utils.map(
"n",
"<leader>di",
"<cmd>DoomInfo<CR>",
opts,
"Doom",
"display_info_dashboard",
"Display information dashboard"
)
-- Plugins
utils.map(

532
lua/doom/modules/built-in/info/init.lua

@ -0,0 +1,532 @@
local utils = require("doom.utils")
local system = require("doom.core.system")
--- @class Info
local info = {}
--- Info buffer namespace
local info_ns = vim.api.nvim_create_namespace("doom_info_ns")
--- Info buffer ID
local info_buffer
--- Current buffer ID, meant to be used for getting buffer information like treesitter parser
local curr_buffer = vim.api.nvim_win_get_buf(0)
--- Doom Nvim banner
local doom_banner = {
" ",
"================= =============== =============== ======== ========",
"\\\\ . . . . . . .\\\\ //. . . . . . .\\\\ //. . . . . . .\\\\ \\\\. . .\\\\// . . //",
"||. . ._____. . .|| ||. . ._____. . .|| ||. . ._____. . .|| || . . .\\/ . . .||",
"|| . .|| ||. . || || . .|| ||. . || || . .|| ||. . || ||. . . . . . . ||",
"||. . || || . .|| ||. . || || . .|| ||. . || || . .|| || . | . . . . .||",
"|| . .|| ||. _-|| ||-_ .|| ||. . || || . .|| ||. _-|| ||-_.|\\ . . . . ||",
"||. . || ||-' || || `-|| || . .|| ||. . || ||-' || || `|\\_ . .|. .||",
"|| . _|| || || || || ||_ . || || . _|| || || || |\\ `-_/| . ||",
"||_-' || .|/ || || \\|. || `-_|| ||_-' || .|/ || || | \\ / |-_.||",
"|| ||_-' || || `-_|| || || ||_-' || || | \\ / | `||",
"|| `' || || `' || || `' || || | \\ / | ||",
"|| .===' `===. .==='.`===. .===' /==. | \\/ | ||",
"|| .==' \\_|-_ `===. .===' _|_ `===. .===' _-|/ `== \\/ | ||",
"|| .==' _-' `-_ `=' _-' `-_ `=' _-' `-_ /| \\/ | ||",
"|| .==' _-' `-__\\._-' `-_./__-' `' |. /| | ||",
"||.==' _-' `' | /==.||",
"==' _-' N E O V I M \\/ `==",
"\\ _-' `-_ /",
" `'' ``' ",
" ",
}
--- System-wide commands required by Doom (some of them are optional)
local additional_executables = { "rg", "fd", "git", "npm", "node", "tree-sitter" }
--- Gets padding with N length
--- @param n number
--- @return string
local function get_padding(n)
return (" "):rep(n)
end
--- Padding levels
local padding_level = {
get_padding(2),
get_padding(4),
get_padding(6),
}
--- Aligns text to center
--- @param text_lines table
--- @param align_mode string
--- @return table
local function align_text(text_lines, align_mode)
local aligned_text = {}
local padding_amount
local window_width = vim.api.nvim_win_get_width(0)
for _, line in ipairs(text_lines) do
if align_mode == "left" then
padding_amount = 2
elseif align_mode == "center" then
padding_amount = math.floor(window_width / 2) - math.floor(line:len() / 2)
end
local padding = get_padding(padding_amount)
table.insert(aligned_text, padding .. line)
end
return aligned_text
end
--- Checks if a system command exists
--- @param command string
--- @return boolean
local function command_exists(command)
return vim.fn.executable(command) == 1
end
--- Extract the basename of given filepath, suffix is not trimed.
local function basename(path)
return string.match(path, string.format(".*%s(.*)", system.sep))
end
--- Get the active language servers, extracted from my galaxyline fork
--- @return string
local get_lsp_clients = function(bufnr)
local msg = "No Active Lsp"
local clients = vim.lsp.buf_get_clients(bufnr)
if next(clients) == nil then
return msg
end
local client_names = ""
for _, client in pairs(clients) do
if string.len(client_names) < 1 then
client_names = client_names .. client.name
else
client_names = client_names .. ", " .. client.name
end
end
return string.len(client_names) > 0 and client_names or msg
end
local function get_doom_info()
local doom_info = {}
----- DOOM INFORMATION ------------------------
-----------------------------------------------
-- Doom version
local doom_version = utils.doom_version
-- Doom branch
local git_branch_handler = io.popen(
require("doom.core.system").git_workspace .. " branch --show-current"
)
local doom_branch = git_branch_handler:read("*a"):gsub("[\r\n]", "")
git_branch_handler:close()
-- Configurations path
local config_path = require("doom.core.config").source
local modules_path = require("doom.core.config.modules").source
local userplugins_path = require("doom.core.config.userplugins").source
----- NVIM INFORMATION ------------------------
-----------------------------------------------
local nvim_dev_version = false
-- Neovim version
local version = vim.version()
if version.minor == 6 then
nvim_dev_version = true
end
local nvim_version = string.format(
"%s.%s.%s %s",
version.major,
version.minor,
version.patch,
nvim_dev_version and "(prerelease)" or ""
)
-- Local commit and last update date
local last_update_handler = io.popen(system.git_workspace .. " show -s --format=%cD")
local last_update_date = last_update_handler:read("*a"):gsub("[\r\n]", "")
last_update_handler:close()
vim.list_extend(doom_info, {
"Doom Nvim Information",
string.format("%s• Neovim version: %s%s", padding_level[1], padding_level[1], nvim_version),
string.format(
"%s• Doom version: %s%s (%s branch)",
padding_level[1],
padding_level[2],
doom_version,
doom_branch
),
string.format("%s• Last update date: %s", padding_level[1], last_update_date),
})
if doom_branch == "develop" then
local commit_handler = io.popen(system.git_workspace .. " rev-parse HEAD")
local current_commit = commit_handler:read("*a"):gsub("[\r\n]", "")
commit_handler:close()
vim.list_extend(doom_info, {
string.format(
"%s• Current commit: %s%s",
padding_level[1],
padding_level[1],
current_commit:sub(current_commit:len() - 6, current_commit:len())
),
})
end
vim.list_extend(doom_info, {
string.format(
"%s• Doom root:%s%s",
padding_level[1],
padding_level[2]:rep(2),
system.doom_root
),
"",
string.format("%s▶ Doom configurations paths", padding_level[1]),
string.format("%s- %s", padding_level[2], config_path),
string.format("%s- %s", padding_level[2], modules_path),
string.format("%s- %s", padding_level[2], userplugins_path),
})
----- TREESITTER INFORMATION ------------------
-----------------------------------------------
vim.list_extend(doom_info, {
"",
string.format("%s▶ Installed treesitter parsers", padding_level[1]),
})
for _, parser in ipairs(require("nvim-treesitter.info").installed_parsers()) do
table.insert(doom_info, string.format("%s- %s", padding_level[2], parser))
end
----- LSP INFORMATION -------------------------
-----------------------------------------------
if
not require("doom.core.functions").is_plugin_disabled("lsp")
and packer_plugins["nvim-lspinstall"]
then
vim.list_extend(doom_info, {
"",
string.format("%s▶ Installed language servers", padding_level[1]),
})
for _, server in ipairs(require("lspinstall").installed_servers()) do
-- Get the real name for the language server because lspinstall names them like the filetype
local server_path = require("lspconfig")[server].cmd[1]
local real_server_name = basename(server_path)
if not real_server_name then
-- If we were unable to get the server executable name then fallback to default server name (filetype)
real_server_name = server
else
-- Fix some server names that are "incorrect"
real_server_name = real_server_name
:gsub("%-language%-server", "")
:gsub("%-langserver", "")
:gsub("typescript", "tsserver")
:gsub("sumneko%-lua", "sumneko_lua")
end
table.insert(doom_info, string.format("%s- %s", padding_level[2], real_server_name))
end
end
return doom_info
end
-- TODO: add treesitter and LSP information like LunarVim
local function get_buffer_info()
local buffer_info = {}
local buffer_ft = vim.api.nvim_buf_get_option(curr_buffer, "filetype")
vim.list_extend(buffer_info, {
"Buffer Information",
string.format(
"%s• %s%s%s",
padding_level[1],
"Is read-only?",
padding_level[3],
vim.api.nvim_buf_get_option(curr_buffer, "readonly") and "yes" or "no"
),
string.format("%s• %s: %s", padding_level[1], "Detected filetype", buffer_ft),
"",
string.format("%s▶ Buffer settings", padding_level[1]),
string.format(
"%s• %s: %s",
padding_level[2],
"Indentation width",
vim.api.nvim_buf_get_option(curr_buffer, "tabstop")
),
string.format(
"%s• %s: %s%s",
padding_level[2],
"File format",
padding_level[3],
vim.api.nvim_buf_get_option(curr_buffer, "fileformat"):upper()
),
string.format(
"%s• %s: %s%s",
padding_level[2],
"File encoding",
padding_level[2],
vim.api.nvim_buf_get_option(curr_buffer, "fileencoding"):upper()
),
"",
----- TREESITTER INFORMATION ------------------
-----------------------------------------------
string.format("%s▶ TreeSitter", padding_level[1]),
string.format(
"%s• %s%s",
padding_level[2],
"Is parser installed?" .. padding_level[2]:rep(3),
utils.has_value(require("nvim-treesitter.info").installed_parsers(), buffer_ft)
and "yes"
or "no"
),
string.format(
"%s• %s %s",
padding_level[2],
"Is indentation enabled?" .. padding_level[2]:rep(2),
require("nvim-treesitter.configs").is_enabled("indent", buffer_ft) and "yes" or "no"
),
string.format(
"%s• %s %s",
padding_level[2],
"Is syntax highlighting enabled?",
require("nvim-treesitter.configs").is_enabled("highlight", buffer_ft) and "yes" or "no"
),
})
----- LSP INFORMATION -------------------------
-----------------------------------------------
if
not require("doom.core.functions").is_plugin_disabled("lsp")
and packer_plugins["nvim-lspinstall"]
then
vim.list_extend(buffer_info, {
"",
string.format("%s▶ Language Servers", padding_level[1]),
string.format(
"%s• %s %s",
padding_level[2],
"Is language server installed?",
utils.has_value(require("lspinstall").installed_servers(), buffer_ft) and "yes" or "no"
),
string.format(
"%s• %s:%s%s",
padding_level[2],
"Active language servers",
padding_level[3],
get_lsp_clients(curr_buffer)
),
})
end
return buffer_info
end
local function get_system_info()
local sys_info = {}
----- OS --------------------------------------
-----------------------------------------------
-- Get the current OS and if the user is running Linux then get also the
-- distribution name, e.g. Manjaro
local sysname = vim.loop.os_uname().sysname
local distro_name
-- If the user is running Linux then get the distribution name
if sysname == "Linux" then
distro_name = vim.trim(
-- PRETTY_NAME="Distribution (Additional info)", e.g.
-- PRETTY_NAME="Fedora 34 (KDE Plasma)"
vim.fn.system(
'cat /etc/os-release | grep "^PRETTY_NAME" | sed '
.. "'s/^PRETTY_NAME=\"//' | sed "
.. "'s/\"//'"
)
)
end
vim.list_extend(sys_info, {
"System Information",
string.format(
"%s• %s: %s%s",
padding_level[1],
"OS",
(padding_level[1] .. padding_level[2]:rep(2)),
sysname
),
})
if distro_name then
vim.list_extend(sys_info, {
string.format(
"%s• %s: %s%s",
padding_level[1],
"Distro",
(padding_level[1] .. padding_level[2]:rep(1)),
distro_name
),
})
end
vim.list_extend(sys_info, {
string.format("%s• %s: %s", padding_level[1], "Architecture", vim.loop.os_uname().machine),
})
----- PROGRAMS --------------------------------
-----------------------------------------------
vim.list_extend(sys_info, {
"",
string.format("%s▶ Programs", padding_level[1]),
})
for _, program in ipairs(additional_executables) do
local opt_message, extra_padding = "", ""
-- We add an extra whitespace to 'no' so we can have an uniform format
local is_installed = command_exists(program) and "yes" or "no"
if (program == "node" or program == "npm") and (not command_exists(program)) then
opt_message = "(Optional, required by some language servers)"
elseif program == "fd" and (not command_exists(program)) then
opt_message = "(Optional, improves performance for many file indexing commands)"
elseif program == "rg" and (not command_exists(program)) then
opt_message = "(Optional, improves performance for many file indexing commands)"
elseif program == "tree-sitter" and (not command_exists(program)) then
opt_message = "(Optional, only needed for :TSInstallFromGrammar, not required for :TSInstall)"
end
-- Add padding after the question, e.g. Found fd? yes/no
-- ^^
-- padding
if program:len() ~= 11 then
extra_padding = get_padding(11 - program:len())
end
table.insert(
sys_info,
string.format(
"%s• Found %s? %s%s%s",
padding_level[2],
program,
extra_padding,
-- Add extra whitespace to "no"
is_installed == "yes" and is_installed or is_installed .. " ",
opt_message:len() < 1 and opt_message or " " .. opt_message
)
)
end
return sys_info
end
--- Set buffer contents
--- @param buffer_id number
local function set_buffer_content(buffer_id)
local content = {}
-- Doom banner
for _, banner_line in ipairs(align_text(doom_banner, "center")) do
table.insert(content, banner_line)
end
-- Doom Nvim information
local doom_info = get_doom_info()
local buffer_info = get_buffer_info()
local system_info = get_system_info()
for _, info_section in ipairs({ doom_info, { "", "" }, buffer_info, { "", "" }, system_info }) do
local aligned_info = align_text(info_section, "left")
vim.list_extend(content, aligned_info)
end
vim.api.nvim_buf_set_lines(buffer_id, 0, info_ns, false, content)
end
local function set_syntax_highlighting(buffer_id)
for lnum, line in ipairs(vim.api.nvim_buf_get_lines(buffer_id, 0, vim.fn.line("$"), true)) do
if lnum < #doom_banner then
vim.api.nvim_buf_add_highlight(buffer_id, -1, "Comment", lnum, 0, -1)
else
local hl = "NONE"
local endl = -1
if line:find("Information") then
-- Information headers
hl = "Title" -- "Structure"
elseif line:find("") then
-- Information headers level 2 (subheaders)
hl = "VariableBuiltin"
elseif line:find(": ") or line:find("? ") then
-- Information fields
hl = "Bold"
if #vim.split(line, "?") < 2 then
-- Highlight only the field name, e.g. 'Version'
endl = vim.split(line, ":")[1]:len()
else
-- Highlight the field name and the '?' character
endl = vim.split(line, "?")[1]:len() + 1
end
end
vim.api.nvim_buf_add_highlight(buffer_id, -1, hl, lnum - 1, 0, endl)
end
end
-- Extra highlights that we can't manually set with nvim_buf_add_highlight
vim.cmd([[
call matchadd("TextSuccessBold", "yes")
call matchadd("TextErrorBold", '\(no\s\)\|\(no$\)\|\(No Active Lsp\)')
call matchadd("Boolean", '\(true\)\|\(false\)')
call matchadd("Operator", '\(\s\+-\s\)')
call matchadd("SpecialComment", '\(\s[a-f0-9]\{7}$\)')
call matchadd("Number", '\s[0-9]\+$')
call matchadd("String", "")
call matchadd("CommentBold", '\(:[A-Za-z]\+\)')
]])
end
--- Open Doom information floating window
info.open = function()
-- Create a new scratch buffer
info_buffer = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_option(info_buffer, "bufhidden", "wipe")
vim.api.nvim_buf_set_option(info_buffer, "filetype", "DoomInfo")
-- Get terminal dimensions
local width = vim.api.nvim_get_option("columns")
local height = vim.api.nvim_get_option("lines")
-- Floating window dimensions and start position
local win_height = math.ceil(height * 0.9 - 2)
local win_width = math.ceil(width * 0.9)
local row = math.ceil((height - win_height) / 2 - 1)
local col = math.ceil((width - win_width) / 2)
-- Set our floating window options
local win_opts = {
style = "minimal", -- Disable most UI options
border = "single", -- Single whitespace padding
relative = "editor", -- Relative to global editor grid
width = win_width, -- Width
height = win_height, -- Height
row = row, -- Row position
col = col, -- Column position
}
-- Create our window with attached buffer
vim.api.nvim_open_win(info_buffer, true, win_opts)
-- Set the buffer contents
set_buffer_content(info_buffer)
-- Set the buffer options
vim.api.nvim_buf_set_option(info_buffer, "modifiable", false)
vim.opt_local.foldtext =
[[substitute(substitute(getline(v:foldstart), '?\s\+', '? ', 'g'),'\\t',repeat('\ ',&tabstop),'g').' ... ' . '(' . (v:foldend - v:foldstart + 1) . ' fields)']]
vim.opt_local.shiftwidth = 6
vim.opt_local.foldignore = [[=\\\|]]
vim.opt_local.foldmethod = "indent"
vim.opt_local.fillchars = {
eob = " ", -- suppress ~ at EndOfBuffer
fold = " ", -- supress dots between folds info
}
-- Set the buffer syntax
set_syntax_highlighting(info_buffer)
end
info.close = function()
vim.api.nvim_buf_delete(info_buffer, {})
end
info.toggle = function()
if info_buffer and vim.fn.bufexists(info_buffer) == 1 then
info.close()
else
info.open()
end
end
return info

2
lua/doom/modules/config/doom-blankline.lua

@ -9,7 +9,7 @@ return function()
and true
or false,
show_first_indent_level = false,
filetype_exclude = { "help", "dashboard", "packer", "norg" },
filetype_exclude = { "help", "dashboard", "packer", "norg", "DoomInfo" },
buftype_exclude = { "terminal" },
})
end

1
lua/doom/modules/config/doom-whichkey.lua

@ -114,6 +114,7 @@ return function()
["b"] = { "Show Doom keybindings" },
["c"] = { "Edit your Doom Nvim configuration" },
["d"] = { "Open Doom Nvim documentation" },
["i"] = { "Open Doom Nvim information dashboard" },
["u"] = { "Update Doom Nvim" },
["r"] = { "Rollback Doom Nvim version" },
["R"] = { "Create crash report" },

Loading…
Cancel
Save