Update generated neovim config
This commit is contained in:
2061
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/ai.lua
Normal file
2061
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/ai.lua
Normal file
File diff suppressed because it is too large
Load Diff
2039
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/align.lua
Normal file
2039
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/align.lua
Normal file
File diff suppressed because it is too large
Load Diff
2152
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/animate.lua
Normal file
2152
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/animate.lua
Normal file
File diff suppressed because it is too large
Load Diff
1547
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/base16.lua
Normal file
1547
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/base16.lua
Normal file
File diff suppressed because it is too large
Load Diff
766
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/basics.lua
Normal file
766
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/basics.lua
Normal file
@ -0,0 +1,766 @@
|
||||
--- *mini.basics* Common configuration presets
|
||||
--- *MiniBasics*
|
||||
---
|
||||
--- MIT License Copyright (c) 2023 Evgeni Chasnovski
|
||||
---
|
||||
--- ==============================================================================
|
||||
---
|
||||
--- Install, create 'init.lua', add `require('mini.basics').setup()` and you
|
||||
--- are good to go.
|
||||
---
|
||||
--- Features:
|
||||
--- - Presets for common options. It will only change option if it wasn't
|
||||
--- manually set before. See more in |MiniBasics.config.options|.
|
||||
---
|
||||
--- - Presets for common mappings. It will only add a mapping if it wasn't
|
||||
--- manually created before. See more in |MiniBasics.config.mappings|.
|
||||
---
|
||||
--- - Presets for common autocommands. See more in |MiniBasics.config.autocommands|.
|
||||
---
|
||||
--- - Reverse compatibility is a high priority. Any decision to change already
|
||||
--- present behavior will be made with great care.
|
||||
---
|
||||
--- Notes:
|
||||
--- - Main goal of this module is to provide a relatively easier way for
|
||||
--- new-ish Neovim users to have better "works out of the box" experience
|
||||
--- while having documented relevant options/mappings/autocommands to study.
|
||||
--- It is based partially on survey among Neovim users and partially is
|
||||
--- coming from personal preferences.
|
||||
---
|
||||
--- However, more seasoned users almost surely will find something useful.
|
||||
---
|
||||
--- Still, it is recommended to read about used options/mappings/autocommands
|
||||
--- and decide if they are needed. The main way to do that is by reading
|
||||
--- Neovim's help pages (linked in help file) and this module's source code
|
||||
--- (thoroughly documented for easier comprehension).
|
||||
---
|
||||
--- # Setup ~
|
||||
---
|
||||
--- This module needs a setup with `require('mini.basics').setup({})` (replace
|
||||
--- `{}` with your `config` table). It will create global Lua table `MiniBasics`
|
||||
--- which you can use for scripting or manually (with `:lua MiniBasics.*`).
|
||||
---
|
||||
--- See |MiniBasics.config| for available config settings.
|
||||
---
|
||||
--- To stop module from showing non-error feedback, set `config.silent = true`.
|
||||
---
|
||||
--- # Comparisons ~
|
||||
---
|
||||
--- - 'tpope/vim-sensible':
|
||||
--- - Most of 'tpope/vim-sensible' is already incorporated as default
|
||||
--- options in Neovim (see |nvim-default|). This module has a much
|
||||
--- broader effect.
|
||||
--- - 'tpope/vim-unimpaired':
|
||||
--- - The 'tpope/vim-unimpaired' has mapping for toggling options with `yo`
|
||||
--- prefix. This module implements similar functionality with `\` prefix
|
||||
--- (see |MiniBasics.config.mappings|).
|
||||
|
||||
---@diagnostic disable:undefined-field
|
||||
|
||||
-- To study source behind presets, search for:
|
||||
-- - `-- Options ---` for `config.options`.
|
||||
-- - `-- Mappings ---` for `config.mappings`.
|
||||
-- - `-- Autocommands ---` for `config.autocommands`.
|
||||
|
||||
-- Module definition ==========================================================
|
||||
local MiniBasics = {}
|
||||
local H = {}
|
||||
|
||||
--- Module setup
|
||||
---
|
||||
---@param config table|nil Module config table. See |MiniBasics.config|.
|
||||
---
|
||||
---@usage >lua
|
||||
--- require('mini.basics').setup() -- use default config
|
||||
--- -- OR
|
||||
--- require('mini.basics').setup({}) -- replace {} with your config table
|
||||
--- <
|
||||
MiniBasics.setup = function(config)
|
||||
-- Export module
|
||||
_G.MiniBasics = MiniBasics
|
||||
|
||||
-- Setup config
|
||||
config = H.setup_config(config)
|
||||
|
||||
-- Apply config
|
||||
H.apply_config(config)
|
||||
end
|
||||
|
||||
--- Module config
|
||||
---
|
||||
--- Default values:
|
||||
---@eval return MiniDoc.afterlines_to_code(MiniDoc.current.eval_section)
|
||||
---@text *MiniBasics.config.options*
|
||||
--- # Options ~
|
||||
---
|
||||
--- Usage example: >lua
|
||||
---
|
||||
--- require('mini.basics').setup({
|
||||
--- options = {
|
||||
--- basic = true,
|
||||
--- extra_ui = true,
|
||||
--- win_borders = 'double',
|
||||
--- }
|
||||
--- })
|
||||
--- <
|
||||
--- ## options.basic ~
|
||||
---
|
||||
--- The `config.options.basic` sets certain options to values which are quite
|
||||
--- commonly used (judging by study of available Neovim pre-configurations,
|
||||
--- public dotfiles, and surveys).
|
||||
--- Any option is changed only if it was not set manually beforehand.
|
||||
--- For exact changes, please see source code ('lua/mini/basics.lua').
|
||||
---
|
||||
--- Here is the list of affected options (put cursor on it and press |CTRL-]|):
|
||||
--- - General:
|
||||
--- - Sets |<Leader>| key to |<Space>|. Be sure to make all Leader mappings
|
||||
--- after this (otherwise they are made with default <Leader>).
|
||||
--- - Runs `:filetype plugin indent on` (see |:filetype-overview|)
|
||||
--- - |backup|
|
||||
--- - |mouse|
|
||||
--- - |undofile|
|
||||
--- - |writebackup|
|
||||
--- - Appearance
|
||||
--- - |breakindent|
|
||||
--- - |cursorline|
|
||||
--- - |fillchars|
|
||||
--- - |linebreak|
|
||||
--- - |number|
|
||||
--- - |ruler|
|
||||
--- - |showmode|
|
||||
--- - |signcolumn|
|
||||
--- - |shortmess|
|
||||
--- - |splitbelow|
|
||||
--- - |splitkeep| (on Neovim>=0.9)
|
||||
--- - |splitright|
|
||||
--- - |termguicolors| (on Neovim<0.10; later versions have it smartly enabled)
|
||||
--- - |wrap|
|
||||
--- - Editing
|
||||
--- - |completeopt|
|
||||
--- - |formatoptions|
|
||||
--- - |ignorecase|
|
||||
--- - |incsearch|
|
||||
--- - |infercase|
|
||||
--- - |smartcase|
|
||||
--- - |smartindent|
|
||||
--- - |virtualedit|
|
||||
---
|
||||
--- ## options.extra_ui ~
|
||||
---
|
||||
--- The `config.options.extra_ui` sets certain options for visual appearance
|
||||
--- which might not be aligned with common preferences, but still worth trying.
|
||||
--- Any option is changed only if it was not set manually beforehand.
|
||||
--- For exact changes, please see source code ('lua/mini/basics.lua').
|
||||
---
|
||||
--- List of affected options:
|
||||
--- - |list|
|
||||
--- - |listchars|
|
||||
--- - |pumblend|
|
||||
--- - |pumheight|
|
||||
--- - |winblend|
|
||||
--- - Runs `:syntax on` (see |:syntax-on|)
|
||||
---
|
||||
--- ## options.win_borders
|
||||
---
|
||||
--- The `config.options.win_borders` updates |fillchars| to have a consistent set of
|
||||
--- characters for window border (`vert`, `horiz`, etc.).
|
||||
---
|
||||
--- Available values:
|
||||
--- - `'bold'` - bold lines.
|
||||
--- - `'dot'` - dot in every cell.
|
||||
--- - `'double'` - double line.
|
||||
--- - `'single'` - single line.
|
||||
--- - `'solid'` - no symbol, only background.
|
||||
---
|
||||
--- *MiniBasics.config.mappings*
|
||||
--- # Mappings ~
|
||||
---
|
||||
--- Usage example: >lua
|
||||
---
|
||||
--- require('mini.basics').setup({
|
||||
--- mappings = {
|
||||
--- basic = true,
|
||||
--- option_toggle_prefix = [[\]],
|
||||
--- windows = true,
|
||||
--- move_with_alt = true,
|
||||
--- }
|
||||
--- })
|
||||
--- <
|
||||
--- If you don't want only some mappings to be made at all, use |vim.keymap.del()|
|
||||
--- after calling |MiniBasics.setup()|.
|
||||
---
|
||||
--- ## mappings.basic ~
|
||||
---
|
||||
--- The `config.mappings.basic` creates mappings for certain commonly mapped actions
|
||||
--- (judging by study of available Neovim pre-configurations and public dotfiles).
|
||||
---
|
||||
--- Some of the mappings override built-in ones to either improve their
|
||||
--- behavior or override its default not very useful action.
|
||||
--- It will only add a mapping if it wasn't manually created before.
|
||||
---
|
||||
--- Here is a table with created mappings : >
|
||||
---
|
||||
--- |Keys | Modes | Description |
|
||||
--- |-------|-----------------|-----------------------------------------------|
|
||||
--- | j | Normal, Visual | Move down by visible lines with no [count] |
|
||||
--- | k | Normal, Visual | Move up by visible lines with no [count] |
|
||||
--- | go | Normal | Add [count] empty lines after cursor |
|
||||
--- | gO | Normal | Add [count] empty lines before cursor |
|
||||
--- | gy | Normal, Visual | Copy to system clipboard |
|
||||
--- | gp | Normal, Visual | Paste from system clipboard |
|
||||
--- | gV | Normal | Visually select latest changed or yanked text |
|
||||
--- | g/ | Visual | Search inside current visual selection |
|
||||
--- | * | Visual | Search forward for current visual selection |
|
||||
--- | # | Visual | Search backward for current visual selection |
|
||||
--- | <C-s> | Normal, Visual, | Save and go to Normal mode |
|
||||
--- | | Insert | |
|
||||
--- <
|
||||
--- Notes:
|
||||
--- - See |[count]| for its meaning.
|
||||
--- - On Neovim>=0.10 mappings for `#` and `*` are not created as their
|
||||
--- enhanced variants are made built-in. See |v_star-default| and |v_#-default|.
|
||||
---
|
||||
--- ## mappings.option_toggle_prefix ~
|
||||
---
|
||||
--- The `config.mappings.option_toggle_prefix` defines a prefix used for
|
||||
--- creating mappings that toggle common options. The result mappings will be
|
||||
--- `<prefix> + <suffix>`. For example, with default value, `\w` will toggle |wrap|.
|
||||
---
|
||||
--- Other viable choices for prefix are
|
||||
--- - `,` (as a mnemonic for several values to toggle).
|
||||
--- - `|` (as a same mnemonic).
|
||||
--- - `yo` (used in 'tpope/vim-unimpaired')
|
||||
--- - Something with |<Leader>| key, like `<Leader>t` (`t` for "toggle"). Note:
|
||||
--- if your prefix contains `<Leader>` key, make sure to set it before
|
||||
--- calling |MiniBasics.setup()| (as is done with default `basic` field of
|
||||
--- |MiniBasics.config.options|).
|
||||
---
|
||||
--- After toggling, there will be a feedback about the current option value if
|
||||
--- prior to `require('mini.basics').setup()` module wasn't silenced (see
|
||||
--- "Silencing" section in |mini.basics|).
|
||||
---
|
||||
--- It will only add a mapping if it wasn't manually created before.
|
||||
---
|
||||
--- Here is a list of suffixes for created toggling mappings (all in Normal mode):
|
||||
---
|
||||
--- - `b` - |'background'|.
|
||||
--- - `c` - |'cursorline'|.
|
||||
--- - `C` - |'cursorcolumn'|.
|
||||
--- - `d` - diagnostic (via |vim.diagnostic| functions).
|
||||
--- - `h` - |'hlsearch'| (or |v:hlsearch| to be precise).
|
||||
--- - `i` - |'ignorecase'|.
|
||||
--- - `l` - |'list'|.
|
||||
--- - `n` - |'number'|.
|
||||
--- - `r` - |'relativenumber'|.
|
||||
--- - `s` - |'spell'|.
|
||||
--- - `w` - |'wrap'|.
|
||||
---
|
||||
--- ## mappings.windows ~
|
||||
---
|
||||
--- The `config.mappings.windows` creates mappings for easiere window manipulation.
|
||||
---
|
||||
--- It will only add a mapping if it wasn't manually created before.
|
||||
---
|
||||
--- Here is a list with created Normal mode mappings (all mappings respect |[count]|):
|
||||
--- - Window navigation:
|
||||
--- - `<C-h>` - focus on left window (see |CTRL-W_H|).
|
||||
--- - `<C-j>` - focus on below window (see |CTRL-W_J|).
|
||||
--- - `<C-k>` - focus on above window (see |CTRL-W_K|).
|
||||
--- - `<C-l>` - focus on right window (see |CTRL-W_L|).
|
||||
--- - Window resize (all use arrow keys; variants of |resize|; all respect |[count]|):
|
||||
--- - `<C-left>` - decrease window width.
|
||||
--- - `<C-down>` - decrease window height.
|
||||
--- - `<C-up>` - increase window height.
|
||||
--- - `<C-right>` - increase window width.
|
||||
---
|
||||
--- ## mappings.move_with_alt
|
||||
---
|
||||
--- The `config.mappings.move_with_alt` creates mappings for a more consistent
|
||||
--- cursor move in Insert, Command, and Terminal modes. For example, it proves
|
||||
--- useful in combination of autopair plugin (like |MiniPairs|) to move right
|
||||
--- outside of inserted pairs (no matter what the pair is).
|
||||
---
|
||||
--- It will only add a mapping if it wasn't manually created before.
|
||||
---
|
||||
--- Here is a list of created mappings (`<M-x>` means `Alt`/`Meta` plus `x`):
|
||||
--- - `<M-h>` - move cursor left. Modes: Insert, Terminal, Command.
|
||||
--- - `<M-j>` - move cursor down. Modes: Insert, Terminal.
|
||||
--- - `<M-k>` - move cursor up. Modes: Insert, Terminal.
|
||||
--- - `<M-l>` - move cursor right. Modes: Insert, Terminal, Command.
|
||||
---
|
||||
--- *MiniBasics.config.autocommands*
|
||||
--- # Autocommands ~
|
||||
---
|
||||
--- Usage example: >lua
|
||||
---
|
||||
--- require('mini.basics').setup({
|
||||
--- autocommands = {
|
||||
--- basic = true,
|
||||
--- relnum_in_visual_mode = true,
|
||||
--- }
|
||||
--- })
|
||||
--- <
|
||||
--- ## autocommands.basic ~
|
||||
---
|
||||
--- The `config.autocommands.basic` creates some common autocommands:
|
||||
---
|
||||
--- - Starts insert mode when opening terminal (see |startinsert| and |TermOpen|).
|
||||
--- - Highlights yanked text for a brief period of time (see
|
||||
--- |vim.highlight.on_yank()| and |TextYankPost|).
|
||||
---
|
||||
--- ## autocommands.relnum_in_visual_mode ~
|
||||
---
|
||||
--- The `config.autocommands.relnum_in_visual_mode` creates autocommands that
|
||||
--- enable |relativenumber| in linewise and blockwise Visual modes and disable
|
||||
--- otherwise. See |ModeChanged|.
|
||||
MiniBasics.config = {
|
||||
-- Options. Set to `false` to disable.
|
||||
options = {
|
||||
-- Basic options ('number', 'ignorecase', and many more)
|
||||
basic = true,
|
||||
|
||||
-- Extra UI features ('winblend', 'cmdheight=0', ...)
|
||||
extra_ui = false,
|
||||
|
||||
-- Presets for window borders ('single', 'double', ...)
|
||||
win_borders = 'default',
|
||||
},
|
||||
|
||||
-- Mappings. Set to `false` to disable.
|
||||
mappings = {
|
||||
-- Basic mappings (better 'jk', save with Ctrl+S, ...)
|
||||
basic = true,
|
||||
|
||||
-- Prefix for mappings that toggle common options ('wrap', 'spell', ...).
|
||||
-- Supply empty string to not create these mappings.
|
||||
option_toggle_prefix = [[\]],
|
||||
|
||||
-- Window navigation with <C-hjkl>, resize with <C-arrow>
|
||||
windows = false,
|
||||
|
||||
-- Move cursor in Insert, Command, and Terminal mode with <M-hjkl>
|
||||
move_with_alt = false,
|
||||
},
|
||||
|
||||
-- Autocommands. Set to `false` to disable
|
||||
autocommands = {
|
||||
-- Basic autocommands (highlight on yank, start Insert in terminal, ...)
|
||||
basic = true,
|
||||
|
||||
-- Set 'relativenumber' only in linewise and blockwise Visual mode
|
||||
relnum_in_visual_mode = false,
|
||||
},
|
||||
|
||||
-- Whether to disable showing non-error feedback
|
||||
silent = false,
|
||||
}
|
||||
--minidoc_afterlines_end
|
||||
|
||||
--- Toggle diagnostic for current buffer
|
||||
---
|
||||
--- This uses |vim.diagnostic| functions per buffer.
|
||||
---
|
||||
---@return string String indicator for new state. Similar to what |:set| `{option}?` shows.
|
||||
MiniBasics.toggle_diagnostic = function()
|
||||
local buf_id = vim.api.nvim_get_current_buf()
|
||||
local is_enabled = H.diagnostic_is_enabled(buf_id)
|
||||
|
||||
local f
|
||||
if vim.fn.has('nvim-0.10') == 1 then
|
||||
f = function(bufnr) vim.diagnostic.enable(not is_enabled, { bufnr = bufnr }) end
|
||||
else
|
||||
f = is_enabled and vim.diagnostic.disable or vim.diagnostic.enable
|
||||
end
|
||||
f(buf_id)
|
||||
|
||||
local new_buf_state = not is_enabled
|
||||
H.buffer_diagnostic_state[buf_id] = new_buf_state
|
||||
|
||||
return new_buf_state and ' diagnostic' or 'nodiagnostic'
|
||||
end
|
||||
|
||||
-- Helper data ================================================================
|
||||
-- Module default config
|
||||
H.default_config = vim.deepcopy(MiniBasics.config)
|
||||
|
||||
-- Diagnostic state per buffer
|
||||
H.buffer_diagnostic_state = {}
|
||||
|
||||
-- Helper functionality =======================================================
|
||||
-- Settings -------------------------------------------------------------------
|
||||
H.setup_config = function(config)
|
||||
-- General idea: if some table elements are not present in user-supplied
|
||||
-- `config`, take them from default config
|
||||
vim.validate({ config = { config, 'table', true } })
|
||||
config = vim.tbl_deep_extend('force', vim.deepcopy(H.default_config), config or {})
|
||||
|
||||
vim.validate({
|
||||
options = { config.options, 'table' },
|
||||
mappings = { config.mappings, 'table' },
|
||||
autocommands = { config.autocommands, 'table' },
|
||||
})
|
||||
|
||||
vim.validate({
|
||||
['options.basic'] = { config.options.basic, 'boolean' },
|
||||
['options.extra_ui'] = { config.options.extra_ui, 'boolean' },
|
||||
['options.win_borders'] = { config.options.win_borders, 'string' },
|
||||
|
||||
['mappings.basic'] = { config.mappings.basic, 'boolean' },
|
||||
['mappings.option_toggle_prefix'] = { config.mappings.option_toggle_prefix, 'string' },
|
||||
['mappings.windows'] = { config.mappings.windows, 'boolean' },
|
||||
['mappings.move_with_alt'] = { config.mappings.move_with_alt, 'boolean' },
|
||||
|
||||
['autocommands.basic'] = { config.autocommands.basic, 'boolean' },
|
||||
['autocommands.relnum_in_visual_mode'] = { config.autocommands.relnum_in_visual_mode, 'boolean' },
|
||||
|
||||
['silent'] = { config.silent, 'boolean' },
|
||||
})
|
||||
|
||||
return config
|
||||
end
|
||||
|
||||
H.apply_config = function(config)
|
||||
MiniBasics.config = config
|
||||
|
||||
H.apply_options(config)
|
||||
H.apply_mappings(config)
|
||||
H.apply_autocommands(config)
|
||||
end
|
||||
|
||||
-- Options --------------------------------------------------------------------
|
||||
--stylua: ignore
|
||||
H.apply_options = function(config)
|
||||
-- Use `local o, opt = vim.o, vim.opt` to copy lines as is.
|
||||
-- Or use `vim.o` and `vim.opt` directly.
|
||||
local o, opt = H.vim_o, H.vim_opt
|
||||
|
||||
-- Basic options
|
||||
if config.options.basic then
|
||||
-- Leader key
|
||||
if vim.g.mapleader == nil then
|
||||
vim.g.mapleader = ' ' -- Use space as the one and only true Leader key
|
||||
end
|
||||
|
||||
-- General
|
||||
o.undofile = true -- Enable persistent undo (see also `:h undodir`)
|
||||
|
||||
o.backup = false -- Don't store backup while overwriting the file
|
||||
o.writebackup = false -- Don't store backup while overwriting the file
|
||||
|
||||
o.mouse = 'a' -- Enable mouse for all available modes
|
||||
|
||||
vim.cmd('filetype plugin indent on') -- Enable all filetype plugins
|
||||
|
||||
-- Appearance
|
||||
o.breakindent = true -- Indent wrapped lines to match line start
|
||||
o.cursorline = true -- Highlight current line
|
||||
o.linebreak = true -- Wrap long lines at 'breakat' (if 'wrap' is set)
|
||||
o.number = true -- Show line numbers
|
||||
o.splitbelow = true -- Horizontal splits will be below
|
||||
o.splitright = true -- Vertical splits will be to the right
|
||||
|
||||
o.ruler = false -- Don't show cursor position in command line
|
||||
o.showmode = false -- Don't show mode in command line
|
||||
o.wrap = false -- Display long lines as just one line
|
||||
|
||||
o.signcolumn = 'yes' -- Always show sign column (otherwise it will shift text)
|
||||
o.fillchars = 'eob: ' -- Don't show `~` outside of buffer
|
||||
|
||||
-- Editing
|
||||
o.ignorecase = true -- Ignore case when searching (use `\C` to force not doing that)
|
||||
o.incsearch = true -- Show search results while typing
|
||||
o.infercase = true -- Infer letter cases for a richer built-in keyword completion
|
||||
o.smartcase = true -- Don't ignore case when searching if pattern has upper case
|
||||
o.smartindent = true -- Make indenting smart
|
||||
|
||||
o.completeopt = 'menuone,noinsert,noselect' -- Customize completions
|
||||
o.virtualedit = 'block' -- Allow going past the end of line in visual block mode
|
||||
o.formatoptions = 'qjl1' -- Don't autoformat comments
|
||||
|
||||
-- Neovim version dependent
|
||||
if vim.fn.has('nvim-0.9') == 1 then
|
||||
opt.shortmess:append('WcC') -- Reduce command line messages
|
||||
o.splitkeep = 'screen' -- Reduce scroll during window split
|
||||
else
|
||||
opt.shortmess:append('Wc') -- Reduce command line messages
|
||||
end
|
||||
|
||||
if vim.fn.has('nvim-0.10') == 0 then
|
||||
o.termguicolors = true -- Enable gui colors
|
||||
end
|
||||
end
|
||||
|
||||
-- Some opinioneted extra UI options
|
||||
if config.options.extra_ui then
|
||||
o.pumblend = 10 -- Make builtin completion menus slightly transparent
|
||||
o.pumheight = 10 -- Make popup menu smaller
|
||||
o.winblend = 10 -- Make floating windows slightly transparent
|
||||
|
||||
-- NOTE: Having `tab` present is needed because `^I` will be shown if
|
||||
-- omitted (documented in `:h listchars`).
|
||||
-- Having it equal to a default value should be less intrusive.
|
||||
o.listchars = 'tab:> ,extends:…,precedes:…,nbsp:␣' -- Define which helper symbols to show
|
||||
o.list = true -- Show some helper symbols
|
||||
|
||||
-- Enable syntax highlighting if it wasn't already (as it is time consuming)
|
||||
if vim.fn.exists("syntax_on") ~= 1 then vim.cmd([[syntax enable]]) end
|
||||
end
|
||||
|
||||
-- Use some common window borders presets
|
||||
local border_chars = H.win_borders_fillchars[config.options.win_borders]
|
||||
if border_chars ~= nil then
|
||||
vim.opt.fillchars:append(border_chars)
|
||||
end
|
||||
end
|
||||
|
||||
H.vim_o = setmetatable({}, {
|
||||
__newindex = function(_, name, value)
|
||||
local was_set = vim.api.nvim_get_option_info(name).was_set
|
||||
if was_set then return end
|
||||
|
||||
vim.o[name] = value
|
||||
end,
|
||||
})
|
||||
|
||||
H.vim_opt = setmetatable({}, {
|
||||
__index = function(_, name)
|
||||
local was_set = vim.api.nvim_get_option_info(name).was_set
|
||||
if was_set then return { append = function() end, remove = function() end } end
|
||||
|
||||
return vim.opt[name]
|
||||
end,
|
||||
})
|
||||
|
||||
--stylua: ignore
|
||||
H.win_borders_fillchars = {
|
||||
bold = 'vert:┃,horiz:━,horizdown:┳,horizup:┻,verthoriz:╋,vertleft:┫,vertright:┣',
|
||||
dot = 'vert:·,horiz:·,horizdown:·,horizup:·,verthoriz:·,vertleft:·,vertright:·',
|
||||
double = 'vert:║,horiz:═,horizdown:╦,horizup:╩,verthoriz:╬,vertleft:╣,vertright:╠',
|
||||
single = 'vert:│,horiz:─,horizdown:┬,horizup:┴,verthoriz:┼,vertleft:┤,vertright:├',
|
||||
solid = 'vert: ,horiz: ,horizdown: ,horizup: ,verthoriz: ,vertleft: ,vertright: ',
|
||||
}
|
||||
|
||||
-- Mappings -------------------------------------------------------------------
|
||||
--stylua: ignore
|
||||
H.apply_mappings = function(config)
|
||||
-- Use `local map = vim.keymap.set` to copy lines as is. Or use it directly.
|
||||
local map = H.keymap_set
|
||||
|
||||
if config.mappings.basic then
|
||||
-- Move by visible lines. Notes:
|
||||
-- - Don't map in Operator-pending mode because it severely changes behavior:
|
||||
-- like `dj` on non-wrapped line will not delete it.
|
||||
-- - Condition on `v:count == 0` to allow easier use of relative line numbers.
|
||||
map({ 'n', 'x' }, 'j', [[v:count == 0 ? 'gj' : 'j']], { expr = true })
|
||||
map({ 'n', 'x' }, 'k', [[v:count == 0 ? 'gk' : 'k']], { expr = true })
|
||||
|
||||
-- Add empty lines before and after cursor line supporting dot-repeat
|
||||
MiniBasics.put_empty_line = function(put_above)
|
||||
-- This has a typical workflow for enabling dot-repeat:
|
||||
-- - On first call it sets `operatorfunc`, caches data, and calls
|
||||
-- `operatorfunc` on current cursor position.
|
||||
-- - On second call it performs task: puts `v:count1` empty lines
|
||||
-- above/below current line.
|
||||
if type(put_above) == 'boolean' then
|
||||
vim.o.operatorfunc = 'v:lua.MiniBasics.put_empty_line'
|
||||
MiniBasics.cache_empty_line = { put_above = put_above }
|
||||
return 'g@l'
|
||||
end
|
||||
|
||||
local target_line = vim.fn.line('.') - (MiniBasics.cache_empty_line.put_above and 1 or 0)
|
||||
vim.fn.append(target_line, vim.fn['repeat']({ '' }, vim.v.count1))
|
||||
end
|
||||
|
||||
-- NOTE: if you don't want to support dot-repeat, use this snippet:
|
||||
-- ```
|
||||
-- map('n', 'gO', "<Cmd>call append(line('.') - 1, repeat([''], v:count1))<CR>")
|
||||
-- map('n', 'go', "<Cmd>call append(line('.'), repeat([''], v:count1))<CR>")
|
||||
-- ```
|
||||
map('n', 'gO', 'v:lua.MiniBasics.put_empty_line(v:true)', { expr = true, desc = 'Put empty line above' })
|
||||
map('n', 'go', 'v:lua.MiniBasics.put_empty_line(v:false)', { expr = true, desc = 'Put empty line below' })
|
||||
|
||||
-- Copy/paste with system clipboard
|
||||
map({ 'n', 'x' }, 'gy', '"+y', { desc = 'Copy to system clipboard' })
|
||||
map( 'n', 'gp', '"+p', { desc = 'Paste from system clipboard' })
|
||||
-- - Paste in Visual with `P` to not copy selected text (`:h v_P`)
|
||||
map( 'x', 'gp', '"+P', { desc = 'Paste from system clipboard' })
|
||||
|
||||
-- Reselect latest changed, put, or yanked text
|
||||
map('n', 'gV', '"`[" . strpart(getregtype(), 0, 1) . "`]"', { expr = true, replace_keycodes = false, desc = 'Visually select changed text' })
|
||||
|
||||
-- Search inside visually highlighted text. Use `silent = false` for it to
|
||||
-- make effect immediately.
|
||||
map('x', 'g/', '<esc>/\\%V', { silent = false, desc = 'Search inside visual selection' })
|
||||
|
||||
-- Search visually selected text (slightly better than builtins in
|
||||
-- Neovim>=0.8 but slightly worse than builtins in Neovim>=0.10)
|
||||
-- TODO: Remove this after compatibility with Neovim=0.9 is dropped
|
||||
if vim.fn.has('nvim-0.10') == 0 then
|
||||
map('x', '*', [[y/\V<C-R>=escape(@", '/\')<CR><CR>]], { desc = 'Search forward' })
|
||||
map('x', '#', [[y?\V<C-R>=escape(@", '?\')<CR><CR>]], { desc = 'Search backward' })
|
||||
end
|
||||
|
||||
-- Alternative way to save and exit in Normal mode.
|
||||
-- NOTE: Adding `redraw` helps with `cmdheight=0` if buffer is not modified
|
||||
map( 'n', '<C-S>', '<Cmd>silent! update | redraw<CR>', { desc = 'Save' })
|
||||
map({ 'i', 'x' }, '<C-S>', '<Esc><Cmd>silent! update | redraw<CR>', { desc = 'Save and go to Normal mode' })
|
||||
end
|
||||
|
||||
local toggle_prefix = config.mappings.option_toggle_prefix
|
||||
if type(toggle_prefix) == 'string' and toggle_prefix ~= '' then
|
||||
local map_toggle = function(lhs, rhs, desc) map('n', toggle_prefix .. lhs, rhs, { desc = desc }) end
|
||||
|
||||
if config.silent then
|
||||
-- Toggle without feedback
|
||||
map_toggle('b', '<Cmd>lua vim.o.bg = vim.o.bg == "dark" and "light" or "dark"<CR>', "Toggle 'background'")
|
||||
map_toggle('c', '<Cmd>setlocal cursorline!<CR>', "Toggle 'cursorline'")
|
||||
map_toggle('C', '<Cmd>setlocal cursorcolumn!<CR>', "Toggle 'cursorcolumn'")
|
||||
map_toggle('d', '<Cmd>lua MiniBasics.toggle_diagnostic()<CR>', 'Toggle diagnostic')
|
||||
map_toggle('h', '<Cmd>let v:hlsearch = 1 - v:hlsearch<CR>', 'Toggle search highlight')
|
||||
map_toggle('i', '<Cmd>setlocal ignorecase!<CR>', "Toggle 'ignorecase'")
|
||||
map_toggle('l', '<Cmd>setlocal list!<CR>', "Toggle 'list'")
|
||||
map_toggle('n', '<Cmd>setlocal number!<CR>', "Toggle 'number'")
|
||||
map_toggle('r', '<Cmd>setlocal relativenumber!<CR>', "Toggle 'relativenumber'")
|
||||
map_toggle('s', '<Cmd>setlocal spell!<CR>', "Toggle 'spell'")
|
||||
map_toggle('w', '<Cmd>setlocal wrap!<CR>', "Toggle 'wrap'")
|
||||
else
|
||||
map_toggle('b', '<Cmd>lua vim.o.bg = vim.o.bg == "dark" and "light" or "dark"; print(vim.o.bg)<CR>', "Toggle 'background'")
|
||||
map_toggle('c', '<Cmd>setlocal cursorline! cursorline?<CR>', "Toggle 'cursorline'")
|
||||
map_toggle('C', '<Cmd>setlocal cursorcolumn! cursorcolumn?<CR>', "Toggle 'cursorcolumn'")
|
||||
map_toggle('d', '<Cmd>lua print(MiniBasics.toggle_diagnostic())<CR>', 'Toggle diagnostic')
|
||||
map_toggle('h', '<Cmd>let v:hlsearch = 1 - v:hlsearch | echo (v:hlsearch ? " " : "no") . "hlsearch"<CR>', 'Toggle search highlight')
|
||||
map_toggle('i', '<Cmd>setlocal ignorecase! ignorecase?<CR>', "Toggle 'ignorecase'")
|
||||
map_toggle('l', '<Cmd>setlocal list! list?<CR>', "Toggle 'list'")
|
||||
map_toggle('n', '<Cmd>setlocal number! number?<CR>', "Toggle 'number'")
|
||||
map_toggle('r', '<Cmd>setlocal relativenumber! relativenumber?<CR>', "Toggle 'relativenumber'")
|
||||
map_toggle('s', '<Cmd>setlocal spell! spell?<CR>', "Toggle 'spell'")
|
||||
map_toggle('w', '<Cmd>setlocal wrap! wrap?<CR>', "Toggle 'wrap'")
|
||||
end
|
||||
end
|
||||
|
||||
if config.mappings.windows then
|
||||
-- Window navigation
|
||||
map('n', '<C-H>', '<C-w>h', { desc = 'Focus on left window' })
|
||||
map('n', '<C-J>', '<C-w>j', { desc = 'Focus on below window' })
|
||||
map('n', '<C-K>', '<C-w>k', { desc = 'Focus on above window' })
|
||||
map('n', '<C-L>', '<C-w>l', { desc = 'Focus on right window' })
|
||||
|
||||
-- Window resize (respecting `v:count`)
|
||||
map('n', '<C-Left>', '"<Cmd>vertical resize -" . v:count1 . "<CR>"', { expr = true, replace_keycodes = false, desc = 'Decrease window width' })
|
||||
map('n', '<C-Down>', '"<Cmd>resize -" . v:count1 . "<CR>"', { expr = true, replace_keycodes = false, desc = 'Decrease window height' })
|
||||
map('n', '<C-Up>', '"<Cmd>resize +" . v:count1 . "<CR>"', { expr = true, replace_keycodes = false, desc = 'Increase window height' })
|
||||
map('n', '<C-Right>', '"<Cmd>vertical resize +" . v:count1 . "<CR>"', { expr = true, replace_keycodes = false, desc = 'Increase window width' })
|
||||
end
|
||||
|
||||
if config.mappings.move_with_alt then
|
||||
-- Move only sideways in command mode. Using `silent = false` makes movements
|
||||
-- to be immediately shown.
|
||||
map('c', '<M-h>', '<Left>', { silent = false, desc = 'Left' })
|
||||
map('c', '<M-l>', '<Right>', { silent = false, desc = 'Right' })
|
||||
|
||||
-- Don't `noremap` in insert mode to have these keybindings behave exactly
|
||||
-- like arrows (crucial inside TelescopePrompt)
|
||||
map('i', '<M-h>', '<Left>', { noremap = false, desc = 'Left' })
|
||||
map('i', '<M-j>', '<Down>', { noremap = false, desc = 'Down' })
|
||||
map('i', '<M-k>', '<Up>', { noremap = false, desc = 'Up' })
|
||||
map('i', '<M-l>', '<Right>', { noremap = false, desc = 'Right' })
|
||||
|
||||
map('t', '<M-h>', '<Left>', { desc = 'Left' })
|
||||
map('t', '<M-j>', '<Down>', { desc = 'Down' })
|
||||
map('t', '<M-k>', '<Up>', { desc = 'Up' })
|
||||
map('t', '<M-l>', '<Right>', { desc = 'Right' })
|
||||
end
|
||||
end
|
||||
|
||||
H.keymap_set = function(modes, lhs, rhs, opts)
|
||||
-- NOTE: Use `<C-H>`, `<C-Up>`, `<M-h>` casing (instead of `<C-h>`, `<C-up>`,
|
||||
-- `<M-H>`) to match the `lhs` of keymap info. Otherwise it will say that
|
||||
-- mapping doesn't exist when in fact it does.
|
||||
if type(modes) == 'string' then modes = { modes } end
|
||||
|
||||
for _, mode in ipairs(modes) do
|
||||
-- Don't map if mapping is already set **globally**
|
||||
local map_info = H.get_map_info(mode, lhs)
|
||||
if not H.is_default_keymap(mode, lhs, map_info) then return end
|
||||
|
||||
-- Map
|
||||
H.map(mode, lhs, rhs, opts)
|
||||
end
|
||||
end
|
||||
|
||||
H.is_default_keymap = function(mode, lhs, map_info)
|
||||
if map_info == nil then return true end
|
||||
local rhs, desc = map_info.rhs or '', map_info.desc or ''
|
||||
|
||||
-- Some mappings are set by default in Neovim
|
||||
if mode == 'n' and lhs == '<C-L>' then return rhs:find('nohl') ~= nil end
|
||||
if mode == 'i' and lhs == '<C-S>' then return desc:find('signature') ~= nil end
|
||||
if mode == 'x' and lhs == '*' then return rhs == [[y/\V<C-R>"<CR>]] end
|
||||
if mode == 'x' and lhs == '#' then return rhs == [[y?\V<C-R>"<CR>]] end
|
||||
end
|
||||
|
||||
H.get_map_info = function(mode, lhs)
|
||||
local keymaps = vim.api.nvim_get_keymap(mode)
|
||||
for _, info in ipairs(keymaps) do
|
||||
if info.lhs == lhs then return info end
|
||||
end
|
||||
end
|
||||
|
||||
-- Autocommands ---------------------------------------------------------------
|
||||
H.apply_autocommands = function(config)
|
||||
local augroup = vim.api.nvim_create_augroup('MiniBasicsAutocommands', {})
|
||||
|
||||
local au = function(event, pattern, callback, desc)
|
||||
vim.api.nvim_create_autocmd(event, { group = augroup, pattern = pattern, callback = callback, desc = desc })
|
||||
end
|
||||
|
||||
if config.autocommands.basic then
|
||||
au('TextYankPost', '*', function() vim.highlight.on_yank() end, 'Highlight yanked text')
|
||||
|
||||
local start_terminal_insert = vim.schedule_wrap(function(data)
|
||||
-- Try to start terminal mode only if target terminal is current
|
||||
if not (vim.api.nvim_get_current_buf() == data.buf and vim.bo.buftype == 'terminal') then return end
|
||||
vim.cmd('startinsert')
|
||||
end)
|
||||
au('TermOpen', 'term://*', start_terminal_insert, 'Start builtin terminal in Insert mode')
|
||||
end
|
||||
|
||||
if config.autocommands.relnum_in_visual_mode then
|
||||
au(
|
||||
'ModeChanged',
|
||||
-- Show relative numbers only when they matter (linewise and blockwise
|
||||
-- selection) and 'number' is set (avoids horizontal flickering)
|
||||
'*:[V\x16]*',
|
||||
function() vim.wo.relativenumber = vim.wo.number end,
|
||||
'Show relative line numbers'
|
||||
)
|
||||
au(
|
||||
'ModeChanged',
|
||||
'[V\x16]*:*',
|
||||
-- Hide relative numbers when neither linewise/blockwise mode is on
|
||||
function() vim.wo.relativenumber = string.find(vim.fn.mode(), '^[V\22]') ~= nil end,
|
||||
'Hide relative line numbers'
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
-- Utilities ------------------------------------------------------------------
|
||||
H.map = function(mode, lhs, rhs, opts)
|
||||
if lhs == '' then return end
|
||||
opts = vim.tbl_deep_extend('force', { silent = true }, opts or {})
|
||||
vim.keymap.set(mode, lhs, rhs, opts)
|
||||
end
|
||||
|
||||
if vim.fn.has('nvim-0.10') == 1 then
|
||||
H.diagnostic_is_enabled = function(buf_id) return vim.diagnostic.is_enabled({ bufnr = buf_id }) end
|
||||
elseif vim.fn.has('nvim-0.9') == 1 then
|
||||
H.diagnostic_is_enabled = function(buf_id) return not vim.diagnostic.is_disabled(buf_id) end
|
||||
else
|
||||
H.diagnostic_is_enabled = function(buf_id)
|
||||
local res = H.buffer_diagnostic_state[buf_id]
|
||||
if res == nil then res = true end
|
||||
return res
|
||||
end
|
||||
end
|
||||
|
||||
return MiniBasics
|
||||
2020
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/bracketed.lua
Normal file
2020
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/bracketed.lua
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,284 @@
|
||||
--- *mini.bufremove* Remove buffers
|
||||
--- *MiniBufremove*
|
||||
---
|
||||
--- MIT License Copyright (c) 2021 Evgeni Chasnovski
|
||||
---
|
||||
--- ==============================================================================
|
||||
---
|
||||
--- Features:
|
||||
--- - Unshow, delete, and wipeout buffer while saving window layout
|
||||
--- (opposite to builtin Neovim's commands).
|
||||
---
|
||||
--- # Setup ~
|
||||
---
|
||||
--- This module doesn't need setup, but it can be done to improve usability.
|
||||
--- Setup with `require('mini.bufremove').setup({})` (replace `{}` with your
|
||||
--- `config` table). It will create global Lua table `MiniBufremove` which you
|
||||
--- can use for scripting or manually (with `:lua MiniBufremove.*`).
|
||||
---
|
||||
--- See |MiniBufremove.config| for `config` structure and default values.
|
||||
---
|
||||
--- This module doesn't have runtime options, so using `vim.b.minibufremove_config`
|
||||
--- will have no effect here.
|
||||
---
|
||||
--- To stop module from showing non-error feedback, set `config.silent = true`.
|
||||
---
|
||||
--- # Notes ~
|
||||
---
|
||||
--- 1. Which buffer to show in window(s) after its current buffer is removed is
|
||||
--- decided by the algorithm:
|
||||
--- - If alternate buffer (see |CTRL-^|) is listed (see |buflisted()|), use it.
|
||||
--- - If previous listed buffer (see |bprevious|) is different, use it.
|
||||
--- - Otherwise create a new one with `nvim_create_buf(true, false)` and use it.
|
||||
---
|
||||
--- # Disabling ~
|
||||
---
|
||||
--- To disable core functionality, set `vim.g.minibufremove_disable` (globally) or
|
||||
--- `vim.b.minibufremove_disable` (for a buffer) to `true`. Considering high
|
||||
--- number of different scenarios and customization intentions, writing exact
|
||||
--- rules for disabling module's functionality is left to user. See
|
||||
--- |mini.nvim-disabling-recipes| for common recipes.
|
||||
|
||||
---@alias __bufremove_return boolean|nil Whether operation was successful. If `nil`, no operation was done.
|
||||
---@alias __bufremove_buf_id number|nil Buffer identifier (see |bufnr()|) to use.
|
||||
--- Default: 0 for current.
|
||||
---@alias __bufremove_force boolean|nil Whether to ignore unsaved changes (using `!` version of
|
||||
--- command). If `false`, calling with unsaved changes will prompt confirm dialog.
|
||||
--- Default: `false`.
|
||||
|
||||
-- Module definition ==========================================================
|
||||
local MiniBufremove = {}
|
||||
local H = {}
|
||||
|
||||
--- Module setup
|
||||
---
|
||||
---@param config table|nil Module config table. See |MiniBufremove.config|.
|
||||
---
|
||||
---@usage >lua
|
||||
--- require('mini.bufremove').setup() -- use default config
|
||||
--- -- OR
|
||||
--- require('mini.bufremove').setup({}) -- replace {} with your config table
|
||||
--- <
|
||||
MiniBufremove.setup = function(config)
|
||||
-- Export module
|
||||
_G.MiniBufremove = MiniBufremove
|
||||
|
||||
-- Setup config
|
||||
config = H.setup_config(config)
|
||||
|
||||
-- Apply config
|
||||
H.apply_config(config)
|
||||
end
|
||||
|
||||
--- Module config
|
||||
---
|
||||
--- Default values:
|
||||
---@eval return MiniDoc.afterlines_to_code(MiniDoc.current.eval_section)
|
||||
MiniBufremove.config = {
|
||||
-- Whether to set Vim's settings for buffers (allow hidden buffers)
|
||||
set_vim_settings = true,
|
||||
|
||||
-- Whether to disable showing non-error feedback
|
||||
silent = false,
|
||||
}
|
||||
--minidoc_afterlines_end
|
||||
|
||||
-- Module functionality =======================================================
|
||||
--- Delete buffer `buf_id` with |:bdelete| after unshowing it
|
||||
---
|
||||
---@param buf_id __bufremove_buf_id
|
||||
---@param force __bufremove_force
|
||||
---
|
||||
---@return __bufremove_return
|
||||
MiniBufremove.delete = function(buf_id, force)
|
||||
if H.is_disabled() then return end
|
||||
|
||||
return H.unshow_and_cmd(buf_id, force, 'bdelete')
|
||||
end
|
||||
|
||||
--- Wipeout buffer `buf_id` with |:bwipeout| after unshowing it
|
||||
---
|
||||
---@param buf_id __bufremove_buf_id
|
||||
---@param force __bufremove_force
|
||||
---
|
||||
---@return __bufremove_return
|
||||
MiniBufremove.wipeout = function(buf_id, force)
|
||||
if H.is_disabled() then return end
|
||||
|
||||
return H.unshow_and_cmd(buf_id, force, 'bwipeout')
|
||||
end
|
||||
|
||||
--- Stop showing buffer `buf_id` in all windows
|
||||
---
|
||||
---@param buf_id __bufremove_buf_id
|
||||
---
|
||||
---@return __bufremove_return
|
||||
MiniBufremove.unshow = function(buf_id)
|
||||
if H.is_disabled() then return end
|
||||
|
||||
buf_id = H.normalize_buf_id(buf_id)
|
||||
|
||||
if not H.is_valid_id(buf_id, 'buffer') then return false end
|
||||
|
||||
vim.tbl_map(MiniBufremove.unshow_in_window, vim.fn.win_findbuf(buf_id))
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
--- Stop showing current buffer of window `win_id`
|
||||
---
|
||||
--- Notes:
|
||||
--- - If `win_id` represents |cmdline-window|, this function will close it.
|
||||
---
|
||||
---@param win_id number|nil Window identifier (see |win_getid()|) to use.
|
||||
--- Default: 0 for current.
|
||||
---
|
||||
---@return __bufremove_return
|
||||
MiniBufremove.unshow_in_window = function(win_id)
|
||||
if H.is_disabled() then return nil end
|
||||
|
||||
win_id = (win_id == nil) and 0 or win_id
|
||||
|
||||
if not H.is_valid_id(win_id, 'window') then return false end
|
||||
|
||||
local cur_buf = vim.api.nvim_win_get_buf(win_id)
|
||||
|
||||
-- Temporary use window `win_id` as current to have Vim's functions working
|
||||
vim.api.nvim_win_call(win_id, function()
|
||||
if vim.fn.getcmdwintype() ~= '' then
|
||||
vim.cmd('close!')
|
||||
return
|
||||
end
|
||||
|
||||
-- Try using alternate buffer
|
||||
local alt_buf = vim.fn.bufnr('#')
|
||||
if alt_buf ~= cur_buf and vim.fn.buflisted(alt_buf) == 1 then
|
||||
vim.api.nvim_win_set_buf(win_id, alt_buf)
|
||||
return
|
||||
end
|
||||
|
||||
-- Try using previous buffer
|
||||
local has_previous = pcall(vim.cmd, 'bprevious')
|
||||
if has_previous and cur_buf ~= vim.api.nvim_win_get_buf(win_id) then return end
|
||||
|
||||
-- Create new listed buffer
|
||||
local new_buf = vim.api.nvim_create_buf(true, false)
|
||||
vim.api.nvim_win_set_buf(win_id, new_buf)
|
||||
end)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
-- Helper data ================================================================
|
||||
-- Module default config
|
||||
H.default_config = vim.deepcopy(MiniBufremove.config)
|
||||
|
||||
-- Helper functionality =======================================================
|
||||
-- Settings -------------------------------------------------------------------
|
||||
H.setup_config = function(config)
|
||||
-- General idea: if some table elements are not present in user-supplied
|
||||
-- `config`, take them from default config
|
||||
vim.validate({ config = { config, 'table', true } })
|
||||
config = vim.tbl_deep_extend('force', vim.deepcopy(H.default_config), config or {})
|
||||
|
||||
vim.validate({
|
||||
set_vim_settings = { config.set_vim_settings, 'boolean' },
|
||||
silent = { config.silent, 'boolean' },
|
||||
})
|
||||
|
||||
return config
|
||||
end
|
||||
|
||||
H.apply_config = function(config)
|
||||
MiniBufremove.config = config
|
||||
|
||||
if config.set_vim_settings then
|
||||
vim.o.hidden = true -- Allow hidden buffers
|
||||
end
|
||||
end
|
||||
|
||||
H.is_disabled = function() return vim.g.minibufremove_disable == true or vim.b.minibufremove_disable == true end
|
||||
|
||||
-- Removing implementation ----------------------------------------------------
|
||||
H.unshow_and_cmd = function(buf_id, force, cmd)
|
||||
buf_id = H.normalize_buf_id(buf_id)
|
||||
if not H.is_valid_id(buf_id, 'buffer') then
|
||||
H.message(buf_id .. ' is not a valid buffer id.')
|
||||
return false
|
||||
end
|
||||
|
||||
if force == nil then force = false end
|
||||
if type(force) ~= 'boolean' then
|
||||
H.message('`force` should be boolean.')
|
||||
return false
|
||||
end
|
||||
|
||||
local fun_name = ({ ['bdelete'] = 'delete', ['bwipeout'] = 'wipeout' })[cmd]
|
||||
if not H.can_remove(buf_id, force, fun_name) then return false end
|
||||
|
||||
-- Unshow buffer from all windows
|
||||
MiniBufremove.unshow(buf_id)
|
||||
|
||||
-- Execute command
|
||||
local command = string.format('%s! %d', cmd, buf_id)
|
||||
-- Use `pcall` here to take care of case where `unshow()` was enough. This
|
||||
-- can happen with 'bufhidden' option values:
|
||||
-- - If `delete` then `unshow()` already `bdelete`d buffer. Without `pcall`
|
||||
-- it gives E516 for `MiniBufremove.delete()` (`wipeout` works).
|
||||
-- - If `wipe` then `unshow()` already `bwipeout`ed buffer. Without `pcall`
|
||||
-- it gives E517 for module's `wipeout()` (still E516 for `delete()`).
|
||||
--
|
||||
-- Also account for executing command in command-line window.
|
||||
-- It gives E11 if trying to execute command. The `unshow()` call should
|
||||
-- close such window but somehow it doesn't seem to happen immediately.
|
||||
local ok, result = pcall(vim.cmd, command)
|
||||
if not (ok or result:find('E516%D') or result:find('E517%D') or result:find('E11%D')) then
|
||||
H.message(result)
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
-- Utilities ------------------------------------------------------------------
|
||||
H.echo = function(msg, is_important)
|
||||
if MiniBufremove.config.silent then return end
|
||||
|
||||
-- Construct message chunks
|
||||
msg = type(msg) == 'string' and { { msg } } or msg
|
||||
table.insert(msg, 1, { '(mini.bufremove) ', 'WarningMsg' })
|
||||
|
||||
-- Echo. Force redraw to ensure that it is effective (`:h echo-redraw`)
|
||||
vim.cmd([[echo '' | redraw]])
|
||||
vim.api.nvim_echo(msg, is_important, {})
|
||||
end
|
||||
|
||||
H.message = function(msg) H.echo(msg, true) end
|
||||
|
||||
H.is_valid_id = function(x, type)
|
||||
local is_valid = false
|
||||
if type == 'buffer' then
|
||||
is_valid = vim.api.nvim_buf_is_valid(x)
|
||||
elseif type == 'window' then
|
||||
is_valid = vim.api.nvim_win_is_valid(x)
|
||||
end
|
||||
|
||||
if not is_valid then H.message(string.format('%s is not a valid %s id.', tostring(x), type)) end
|
||||
return is_valid
|
||||
end
|
||||
|
||||
-- Check if buffer can be removed with `MiniBufremove.fun_name` function
|
||||
H.can_remove = function(buf_id, force, fun_name)
|
||||
if force or not vim.bo[buf_id].modified then return true end
|
||||
local msg = string.format('Buffer %d has unsaved changes. Do you want to force %s?', buf_id, fun_name)
|
||||
return vim.fn.confirm(msg, '&No\n&Yes', 1, 'Question') == 2
|
||||
end
|
||||
|
||||
-- Compute 'true' buffer id (strictly positive integer). Treat `nil` and 0 as
|
||||
-- current buffer.
|
||||
H.normalize_buf_id = function(buf_id)
|
||||
if buf_id == nil or buf_id == 0 then return vim.api.nvim_get_current_buf() end
|
||||
return buf_id
|
||||
end
|
||||
|
||||
return MiniBufremove
|
||||
1977
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/clue.lua
Normal file
1977
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/clue.lua
Normal file
File diff suppressed because it is too large
Load Diff
2414
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/colors.lua
Normal file
2414
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/colors.lua
Normal file
File diff suppressed because it is too large
Load Diff
566
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/comment.lua
Normal file
566
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/comment.lua
Normal file
@ -0,0 +1,566 @@
|
||||
--- *mini.comment* Comment lines
|
||||
--- *MiniComment*
|
||||
---
|
||||
--- MIT License Copyright (c) 2021 Evgeni Chasnovski
|
||||
---
|
||||
--- ==============================================================================
|
||||
---
|
||||
--- Features:
|
||||
--- - Commenting in Normal mode respects |count| and is dot-repeatable.
|
||||
---
|
||||
--- - Comment structure by default is inferred from 'commentstring': either
|
||||
--- from current buffer or from locally active tree-sitter language (only on
|
||||
--- Neovim>=0.9). It can be customized via `options.custom_commentstring`
|
||||
--- (see |MiniComment.config| for details).
|
||||
---
|
||||
--- - Allows custom hooks before and after successful commenting.
|
||||
---
|
||||
--- - Configurable options for some nuanced behavior.
|
||||
---
|
||||
--- What it doesn't do:
|
||||
--- - Block and sub-line comments. This will only support per-line commenting.
|
||||
---
|
||||
--- - Handle indentation with mixed tab and space.
|
||||
---
|
||||
--- - Preserve trailing whitespace in empty lines.
|
||||
---
|
||||
--- Notes:
|
||||
--- - To use tree-sitter aware commenting, global value of 'commentstring'
|
||||
--- should be `''` (empty string). This is the default value in Neovim>=0.9,
|
||||
--- so make sure to not set it manually.
|
||||
---
|
||||
--- # Setup ~
|
||||
---
|
||||
--- This module needs a setup with `require('mini.comment').setup({})` (replace
|
||||
--- `{}` with your `config` table). It will create global Lua table
|
||||
--- `MiniComment` which you can use for scripting or manually (with
|
||||
--- `:lua MiniComment.*`).
|
||||
---
|
||||
--- See |MiniComment.config| for `config` structure and default values.
|
||||
---
|
||||
--- You can override runtime config settings locally to buffer inside
|
||||
--- `vim.b.minicomment_config` which should have same structure as
|
||||
--- `MiniComment.config`. See |mini.nvim-buffer-local-config| for more details.
|
||||
---
|
||||
--- # Disabling ~
|
||||
---
|
||||
--- To disable core functionality, set `vim.g.minicomment_disable` (globally) or
|
||||
--- `vim.b.minicomment_disable` (for a buffer) to `true`. Considering high number
|
||||
--- of different scenarios and customization intentions, writing exact rules
|
||||
--- for disabling module's functionality is left to user. See
|
||||
--- |mini.nvim-disabling-recipes| for common recipes.
|
||||
|
||||
-- Module definition ==========================================================
|
||||
local MiniComment = {}
|
||||
local H = {}
|
||||
|
||||
--- Module setup
|
||||
---
|
||||
---@param config table|nil Module config table. See |MiniComment.config|.
|
||||
---
|
||||
---@usage >lua
|
||||
--- require('mini.comment').setup() -- use default config
|
||||
--- -- OR
|
||||
--- require('mini.comment').setup({}) -- replace {} with your config table
|
||||
--- <
|
||||
MiniComment.setup = function(config)
|
||||
-- Export module
|
||||
_G.MiniComment = MiniComment
|
||||
|
||||
-- Setup config
|
||||
config = H.setup_config(config)
|
||||
|
||||
-- Apply config
|
||||
H.apply_config(config)
|
||||
end
|
||||
|
||||
--- Module config
|
||||
---
|
||||
--- Default values:
|
||||
---@eval return MiniDoc.afterlines_to_code(MiniDoc.current.eval_section)
|
||||
---@text # Options ~
|
||||
---
|
||||
--- ## Custom commentstring ~
|
||||
---
|
||||
--- `options.custom_commentstring` can be a function customizing 'commentstring'
|
||||
--- option used to infer comment structure. It is called once before every
|
||||
--- commenting action with the following arguments:
|
||||
--- - `ref_position` - position at which to compute 'commentstring' (might be
|
||||
--- relevant for a text with locally different commenting rules). Its structure
|
||||
--- is the same as `opts.ref_position` in |MiniComment.toggle_lines()|.
|
||||
---
|
||||
--- Its output should be a valid 'commentstring' (string containing `%s`).
|
||||
---
|
||||
--- If not set or the output is `nil`, |MiniComment.get_commentstring()| is used.
|
||||
---
|
||||
--- For example, this option can be used to always use buffer 'commentstring'
|
||||
--- even in case of present active tree-sitter parser: >lua
|
||||
---
|
||||
--- require('mini.comment').setup({
|
||||
--- options = {
|
||||
--- custom_commentstring = function() return vim.bo.commentstring end,
|
||||
--- }
|
||||
--- })
|
||||
--- <
|
||||
--- # Hooks ~
|
||||
---
|
||||
--- `hooks.pre` and `hooks.post` functions are executed before and after successful
|
||||
--- commenting action (toggle or computing textobject). They will be called
|
||||
--- with a single table argument which has the following fields:
|
||||
--- - <action> `(string)` - action name. One of "toggle" (when actual toggle
|
||||
--- direction is yet unknown), "comment", "uncomment", "textobject".
|
||||
--- - <line_start> `(number|nil)` - action start line. Can be absent if yet unknown.
|
||||
--- - <line_end> `(number|nil)` - action end line. Can be absent if yet unknown.
|
||||
--- - <ref_position> `(table|nil)` - reference position.
|
||||
---
|
||||
--- Notes:
|
||||
--- - Changing 'commentstring' in `hooks.pre` is allowed and will take effect.
|
||||
--- - If hook returns `false`, any further action is terminated.
|
||||
MiniComment.config = {
|
||||
-- Options which control module behavior
|
||||
options = {
|
||||
-- Function to compute custom 'commentstring' (optional)
|
||||
custom_commentstring = nil,
|
||||
|
||||
-- Whether to ignore blank lines when commenting
|
||||
ignore_blank_line = false,
|
||||
|
||||
-- Whether to recognize as comment only lines without indent
|
||||
start_of_line = false,
|
||||
|
||||
-- Whether to force single space inner padding for comment parts
|
||||
pad_comment_parts = true,
|
||||
},
|
||||
|
||||
-- Module mappings. Use `''` (empty string) to disable one.
|
||||
mappings = {
|
||||
-- Toggle comment (like `gcip` - comment inner paragraph) for both
|
||||
-- Normal and Visual modes
|
||||
comment = 'gc',
|
||||
|
||||
-- Toggle comment on current line
|
||||
comment_line = 'gcc',
|
||||
|
||||
-- Toggle comment on visual selection
|
||||
comment_visual = 'gc',
|
||||
|
||||
-- Define 'comment' textobject (like `dgc` - delete whole comment block)
|
||||
-- Works also in Visual mode if mapping differs from `comment_visual`
|
||||
textobject = 'gc',
|
||||
},
|
||||
|
||||
-- Hook functions to be executed at certain stage of commenting
|
||||
hooks = {
|
||||
-- Before successful commenting. Does nothing by default.
|
||||
pre = function() end,
|
||||
-- After successful commenting. Does nothing by default.
|
||||
post = function() end,
|
||||
},
|
||||
}
|
||||
--minidoc_afterlines_end
|
||||
|
||||
-- Module functionality =======================================================
|
||||
--- Main function to be mapped
|
||||
---
|
||||
--- It is meant to be used in expression mappings (see |map-<expr>|) to enable
|
||||
--- dot-repeatability and commenting on range. There is no need to do this
|
||||
--- manually, everything is done inside |MiniComment.setup()|.
|
||||
---
|
||||
--- It has a somewhat unintuitive logic (because of how expression mapping with
|
||||
--- dot-repeatability works): it should be called without arguments inside
|
||||
--- expression mapping and with argument when action should be performed.
|
||||
---
|
||||
---@param mode string|nil Optional string with 'operatorfunc' mode (see |g@|).
|
||||
---
|
||||
---@return string|nil 'g@' if called without argument, '' otherwise (but after
|
||||
--- performing action).
|
||||
MiniComment.operator = function(mode)
|
||||
if H.is_disabled() then return '' end
|
||||
|
||||
-- If used without arguments inside expression mapping:
|
||||
-- - Set itself as `operatorfunc` to be called later to perform action.
|
||||
-- - Return 'g@' which will then be executed resulting into waiting for a
|
||||
-- motion or text object. This textobject will then be recorded using `'[`
|
||||
-- and `']` marks. After that, `operatorfunc` is called with `mode` equal
|
||||
-- to one of "line", "char", or "block".
|
||||
-- NOTE: setting `operatorfunc` inside this function enables usage of 'count'
|
||||
-- like `10gc_` toggles comments of 10 lines below (starting with current).
|
||||
if mode == nil then
|
||||
vim.o.operatorfunc = 'v:lua.MiniComment.operator'
|
||||
return 'g@'
|
||||
end
|
||||
|
||||
-- If called with non-nil `mode`, get target region and act on it
|
||||
-- This also works in expression mapping in Visual mode, as `g@` seems to
|
||||
-- place these marks on start and end of visual selection
|
||||
local mark_left, mark_right = '[', ']'
|
||||
local lnum_from, col_from = unpack(vim.api.nvim_buf_get_mark(0, mark_left))
|
||||
local lnum_to, col_to = unpack(vim.api.nvim_buf_get_mark(0, mark_right))
|
||||
|
||||
-- Do nothing if "from" mark is after "to" (like in empty textobject)
|
||||
if (lnum_from > lnum_to) or (lnum_from == lnum_to and col_from > col_to) then return end
|
||||
|
||||
-- NOTE: use cursor position as reference for possibly computing local
|
||||
-- tree-sitter-based 'commentstring'. Recompute every time for a proper
|
||||
-- dot-repeat. In Visual and sometimes Normal mode it uses left position.
|
||||
local cursor = vim.api.nvim_win_get_cursor(0)
|
||||
MiniComment.toggle_lines(lnum_from, lnum_to, { ref_position = { cursor[1], cursor[2] + 1 } })
|
||||
return ''
|
||||
end
|
||||
|
||||
--- Toggle comments between two line numbers
|
||||
---
|
||||
--- It uncomments if lines are comment (every line is a comment) and comments
|
||||
--- otherwise. It respects indentation and doesn't insert trailing
|
||||
--- whitespace. Toggle commenting not in visual mode is also dot-repeatable
|
||||
--- and respects |count|.
|
||||
---
|
||||
--- # Notes ~
|
||||
---
|
||||
--- - Comment structure is inferred from buffer's 'commentstring' option or
|
||||
--- local language of tree-sitter parser (if active; only on Neovim>=0.9).
|
||||
---
|
||||
--- - Call to this function will remove all |extmarks| from target range.
|
||||
---
|
||||
---@param line_start number Start line number (inclusive from 1 to number of lines).
|
||||
---@param line_end number End line number (inclusive from 1 to number of lines).
|
||||
---@param opts table|nil Options. Possible fields:
|
||||
--- - <ref_position> `(table)` - A two-value array with `{ row, col }` (both
|
||||
--- starting at 1) of reference position at which 'commentstring' value
|
||||
--- will be computed. Default: `{ line_start, 1 }`.
|
||||
MiniComment.toggle_lines = function(line_start, line_end, opts)
|
||||
if H.is_disabled() then return end
|
||||
|
||||
opts = opts or {}
|
||||
local ref_position = vim.deepcopy(opts.ref_position) or { line_start, 1 }
|
||||
|
||||
local n_lines = vim.api.nvim_buf_line_count(0)
|
||||
if not (1 <= line_start and line_start <= n_lines and 1 <= line_end and line_end <= n_lines) then
|
||||
error('(mini.comment) `line_start` and `line_end` should be within range [1; ' .. n_lines .. '].')
|
||||
end
|
||||
if not (line_start <= line_end) then
|
||||
error('(mini.comment) `line_start` should be less than or equal to `line_end`.')
|
||||
end
|
||||
|
||||
local config = H.get_config()
|
||||
local hook_arg = { action = 'toggle', line_start = line_start, line_end = line_end, ref_position = ref_position }
|
||||
if config.hooks.pre(hook_arg) == false then return end
|
||||
|
||||
local parts = H.get_comment_parts(ref_position, config.options)
|
||||
local lines = vim.api.nvim_buf_get_lines(0, line_start - 1, line_end, false)
|
||||
local indent, is_comment = H.get_lines_info(lines, parts, config.options)
|
||||
|
||||
local f = is_comment and H.make_uncomment_function(parts) or H.make_comment_function(parts, indent, config.options)
|
||||
|
||||
-- NOTE: Direct of `nvim_buf_set_lines()` essentially removes (squashes to
|
||||
-- empty range at either side of the region) both regular and extended marks
|
||||
-- inside region. It can be resolved at least in the following ways:
|
||||
-- 1. Use `lockmarks`. Preserves regular but does nothing for extmarks.
|
||||
-- 2. Use `vim.fn.setline(line_start, new_lines)`. Preserves regular marks,
|
||||
-- but squashes extmarks within a single line.
|
||||
-- 3. Refactor to use precise editing of lines with `nvim_buf_set_text()`.
|
||||
-- Preserves both regular and extended marks.
|
||||
--
|
||||
-- But:
|
||||
-- - Options 2 and 3 are **significantly** slower for a large-ish regions.
|
||||
-- Toggle of ~4000 lines takes 20 ms for 1, 200 ms for 2, 400 ms for 3.
|
||||
--
|
||||
-- - Preserving extmarks is not a universally good thing to do. It looks like
|
||||
-- a good idea for extmarks which are not used for directly highlighting
|
||||
-- text (like for 'mini.diff' signs or smartly tracking buffer position).
|
||||
-- However, preserving extmarks is not 100% desirable when they highlight
|
||||
-- text area, as every comment toggle at least results in a flickering
|
||||
-- due to those extmarks still highlighting a (un)commented region.
|
||||
-- Main example is LSP semantic token highlighting. Although it can have
|
||||
-- special treatment (precisely clear those extmarks in the target region),
|
||||
-- it is not 100% effective (they are restored after undo, again resulting
|
||||
-- into flicker) and there might be more unnoticed issues.
|
||||
--
|
||||
-- So all in all, computing and replacing whole lines with `lockmarks` is the
|
||||
-- best compromise so far. It also aligns with treating "toggle comment" in
|
||||
-- a semantic way (those lines lines now have completely different meaning)
|
||||
-- rather than in a text edit way (add comment parts to those lines).
|
||||
_G._from, _G._to, _G._lines = line_start - 1, line_end, vim.tbl_map(f, lines)
|
||||
vim.cmd('lockmarks lua pcall(vim.api.nvim_buf_set_lines, 0, _G._from, _G._to, false, _G._lines)')
|
||||
_G._from, _G._to, _G._lines = nil, nil, nil
|
||||
|
||||
hook_arg.action = is_comment and 'uncomment' or 'comment'
|
||||
if config.hooks.post(hook_arg) == false then return end
|
||||
end
|
||||
|
||||
--- Select comment textobject
|
||||
---
|
||||
--- This selects all commented lines adjacent to cursor line (if it itself is
|
||||
--- commented). Designed to be used with operator mode mappings (see |mapmode-o|).
|
||||
MiniComment.textobject = function()
|
||||
if H.is_disabled() then return end
|
||||
|
||||
local config = H.get_config()
|
||||
local hook_args = { action = 'textobject' }
|
||||
if config.hooks.pre(hook_args) == false then return end
|
||||
|
||||
local lnum_cur = vim.fn.line('.')
|
||||
local parts = H.get_comment_parts({ lnum_cur, vim.fn.col('.') }, config.options)
|
||||
local comment_check = H.make_comment_check(parts, config.options)
|
||||
local lnum_from, lnum_to
|
||||
|
||||
if comment_check(vim.fn.getline(lnum_cur)) then
|
||||
lnum_from = lnum_cur
|
||||
while (lnum_from >= 2) and comment_check(vim.fn.getline(lnum_from - 1)) do
|
||||
lnum_from = lnum_from - 1
|
||||
end
|
||||
|
||||
lnum_to = lnum_cur
|
||||
local n_lines = vim.api.nvim_buf_line_count(0)
|
||||
while (lnum_to <= n_lines - 1) and comment_check(vim.fn.getline(lnum_to + 1)) do
|
||||
lnum_to = lnum_to + 1
|
||||
end
|
||||
|
||||
local is_visual = vim.tbl_contains({ 'v', 'V', '\22' }, vim.fn.mode())
|
||||
if is_visual then vim.cmd('normal! \27') end
|
||||
|
||||
-- This visual selection doesn't seem to change `'<` and `'>` marks when
|
||||
-- executed as `onoremap` mapping
|
||||
vim.cmd('normal! ' .. lnum_from .. 'GV' .. lnum_to .. 'G')
|
||||
end
|
||||
|
||||
hook_args.line_start, hook_args.line_end = lnum_from, lnum_to
|
||||
if config.hooks.post(hook_args) == false then return end
|
||||
end
|
||||
|
||||
--- Get 'commentstring'
|
||||
---
|
||||
--- This function represents default approach of computing relevant
|
||||
--- 'commentstring' option in current buffer. Used to infer comment structure.
|
||||
---
|
||||
--- It has the following logic:
|
||||
--- - (Only on Neovim>=0.9) If there is an active tree-sitter parser, try to get
|
||||
--- 'commentstring' from the local language at `ref_position`.
|
||||
---
|
||||
--- - If first step is not successful, use buffer's 'commentstring' directly.
|
||||
---
|
||||
---@param ref_position table Reference position inside current buffer at which
|
||||
--- to compute 'commentstring'. Same structure as `opts.ref_position`
|
||||
--- in |MiniComment.toggle_lines()|.
|
||||
---
|
||||
---@return string Relevant value of 'commentstring'.
|
||||
MiniComment.get_commentstring = function(ref_position)
|
||||
local buf_cs = vim.bo.commentstring
|
||||
|
||||
-- Neovim<0.9 can only have buffer 'commentstring'
|
||||
if vim.fn.has('nvim-0.9') == 0 then return buf_cs end
|
||||
|
||||
local has_ts_parser, ts_parser = pcall(vim.treesitter.get_parser)
|
||||
if not has_ts_parser then return buf_cs end
|
||||
|
||||
-- Try to get 'commentstring' associated with local tree-sitter language.
|
||||
-- This is useful for injected languages (like markdown with code blocks).
|
||||
-- Sources:
|
||||
-- - https://github.com/neovim/neovim/pull/22634#issue-1620078948
|
||||
-- - https://github.com/neovim/neovim/pull/22643
|
||||
local row, col = ref_position[1] - 1, ref_position[2] - 1
|
||||
local ref_range = { row, col, row, col + 1 }
|
||||
|
||||
-- - Get 'commentstring' from the deepest LanguageTree which both contains
|
||||
-- reference range and has valid 'commentstring' (meaning it has at least
|
||||
-- one associated 'filetype' with valid 'commentstring').
|
||||
-- In simple cases using `parser:language_for_range()` would be enough, but
|
||||
-- it fails for languages without valid 'commentstring' (like 'comment').
|
||||
local ts_cs, res_level = nil, 0
|
||||
local traverse
|
||||
|
||||
traverse = function(lang_tree, level)
|
||||
if not lang_tree:contains(ref_range) then return end
|
||||
|
||||
local lang = lang_tree:lang()
|
||||
local filetypes = vim.treesitter.language.get_filetypes(lang)
|
||||
for _, ft in ipairs(filetypes) do
|
||||
-- Using `vim.filetype.get_option()` for performance as it has caching
|
||||
local cur_cs = vim.filetype.get_option(ft, 'commentstring')
|
||||
if type(cur_cs) == 'string' and cur_cs ~= '' and level > res_level then ts_cs = cur_cs end
|
||||
end
|
||||
|
||||
for _, child_lang_tree in pairs(lang_tree:children()) do
|
||||
traverse(child_lang_tree, level + 1)
|
||||
end
|
||||
end
|
||||
traverse(ts_parser, 1)
|
||||
|
||||
return ts_cs or buf_cs
|
||||
end
|
||||
|
||||
-- Helper data ================================================================
|
||||
-- Module default config
|
||||
H.default_config = vim.deepcopy(MiniComment.config)
|
||||
|
||||
-- Helper functionality =======================================================
|
||||
-- Settings -------------------------------------------------------------------
|
||||
H.setup_config = function(config)
|
||||
-- General idea: if some table elements are not present in user-supplied
|
||||
-- `config`, take them from default config
|
||||
vim.validate({ config = { config, 'table', true } })
|
||||
config = vim.tbl_deep_extend('force', vim.deepcopy(H.default_config), config or {})
|
||||
|
||||
-- Validate per nesting level to produce correct error message
|
||||
vim.validate({
|
||||
options = { config.options, 'table' },
|
||||
mappings = { config.mappings, 'table' },
|
||||
hooks = { config.hooks, 'table' },
|
||||
})
|
||||
|
||||
vim.validate({
|
||||
['options.custom_commentstring'] = { config.options.custom_commentstring, 'function', true },
|
||||
['options.ignore_blank_line'] = { config.options.ignore_blank_line, 'boolean' },
|
||||
['options.start_of_line'] = { config.options.start_of_line, 'boolean' },
|
||||
['options.pad_comment_parts'] = { config.options.pad_comment_parts, 'boolean' },
|
||||
['mappings.comment'] = { config.mappings.comment, 'string' },
|
||||
['mappings.comment_line'] = { config.mappings.comment_line, 'string' },
|
||||
['mappings.comment_visual'] = { config.mappings.comment_visual, 'string' },
|
||||
['mappings.textobject'] = { config.mappings.textobject, 'string' },
|
||||
['hooks.pre'] = { config.hooks.pre, 'function' },
|
||||
['hooks.post'] = { config.hooks.post, 'function' },
|
||||
})
|
||||
|
||||
return config
|
||||
end
|
||||
|
||||
H.apply_config = function(config)
|
||||
MiniComment.config = config
|
||||
|
||||
-- Make mappings
|
||||
local operator_rhs = function() return MiniComment.operator() end
|
||||
H.map('n', config.mappings.comment, operator_rhs, { expr = true, desc = 'Comment' })
|
||||
H.map('x', config.mappings.comment_visual, operator_rhs, { expr = true, desc = 'Comment selection' })
|
||||
H.map(
|
||||
'n',
|
||||
config.mappings.comment_line,
|
||||
function() return MiniComment.operator() .. '_' end,
|
||||
{ expr = true, desc = 'Comment line' }
|
||||
)
|
||||
-- Use `<Cmd>...<CR>` to have proper dot-repeat
|
||||
-- See https://github.com/neovim/neovim/issues/23406
|
||||
local modes = config.mappings.textobject == config.mappings.comment_visual and { 'o' } or { 'x', 'o' }
|
||||
H.map(modes, config.mappings.textobject, '<Cmd>lua MiniComment.textobject()<CR>', { desc = 'Comment textobject' })
|
||||
end
|
||||
|
||||
H.is_disabled = function() return vim.g.minicomment_disable == true or vim.b.minicomment_disable == true end
|
||||
|
||||
H.get_config = function(config)
|
||||
return vim.tbl_deep_extend('force', MiniComment.config, vim.b.minicomment_config or {}, config or {})
|
||||
end
|
||||
|
||||
-- Core implementations -------------------------------------------------------
|
||||
H.get_comment_parts = function(ref_position, options)
|
||||
local cs
|
||||
if vim.is_callable(options.custom_commentstring) then cs = options.custom_commentstring(ref_position) end
|
||||
cs = cs or MiniComment.get_commentstring(ref_position)
|
||||
|
||||
if cs == nil or cs == '' then
|
||||
vim.api.nvim_echo({ { '(mini.comment) ', 'WarningMsg' }, { [[Option 'commentstring' is empty.]] } }, true, {})
|
||||
return { left = '', right = '' }
|
||||
end
|
||||
|
||||
if not (type(cs) == 'string' and string.find(cs, '%%s') ~= nil) then
|
||||
H.error(vim.inspect(cs) .. " is not a valid 'commentstring'.")
|
||||
end
|
||||
|
||||
-- Structure of 'commentstring': <left part> <%s> <right part>
|
||||
local left, right = string.match(cs, '^(.-)%%s(.-)$')
|
||||
|
||||
-- Force single space padding if requested
|
||||
if options.pad_comment_parts then
|
||||
left, right = vim.trim(left), vim.trim(right)
|
||||
left, right = left == '' and '' or (left .. ' '), right == '' and '' or (' ' .. right)
|
||||
end
|
||||
return { left = left, right = right }
|
||||
end
|
||||
|
||||
H.make_comment_check = function(parts, options)
|
||||
local l_esc, r_esc = vim.pesc(parts.left), vim.pesc(parts.right)
|
||||
local prefix = options.start_of_line and '' or '%s-'
|
||||
|
||||
-- Commented line has the following structure:
|
||||
-- <whitespace> <trimmed left> <anything> <trimmed right> <whitespace>
|
||||
local regex = '^' .. prefix .. vim.trim(l_esc) .. '.*' .. vim.trim(r_esc) .. '%s-$'
|
||||
|
||||
return function(line) return string.find(line, regex) ~= nil end
|
||||
end
|
||||
|
||||
H.get_lines_info = function(lines, parts, options)
|
||||
local comment_check = H.make_comment_check(parts, options)
|
||||
|
||||
local is_commented = true
|
||||
local indent, indent_width = nil, math.huge
|
||||
|
||||
for _, l in ipairs(lines) do
|
||||
-- Update lines indent: minimum of all indents except blank lines
|
||||
local _, indent_width_cur, indent_cur = string.find(l, '^(%s*)')
|
||||
|
||||
-- Ignore blank lines completely when making a decision
|
||||
if indent_width_cur < l:len() then
|
||||
-- NOTE: Copying actual indent instead of recreating it with `indent_width`
|
||||
-- allows to handle both tabs and spaces
|
||||
if indent_width_cur < indent_width then
|
||||
indent_width, indent = indent_width_cur, indent_cur
|
||||
end
|
||||
|
||||
-- Update comment info: commented if every non-blank line is commented
|
||||
if is_commented then is_commented = comment_check(l) end
|
||||
end
|
||||
end
|
||||
|
||||
-- `indent` can still be `nil` in case all `lines` are empty
|
||||
return indent or '', is_commented
|
||||
end
|
||||
|
||||
H.make_comment_function = function(parts, indent, options)
|
||||
local prefix = options.start_of_line and (parts.left .. indent) or (indent .. parts.left)
|
||||
local nonindent_start = string.len(indent) + 1
|
||||
local suffix = parts.right
|
||||
|
||||
local blank_comment = indent .. vim.trim(parts.left) .. vim.trim(parts.right)
|
||||
local ignore_blank_line = options.ignore_blank_line
|
||||
|
||||
return function(line)
|
||||
if H.is_blank(line) then return ignore_blank_line and line or blank_comment end
|
||||
|
||||
return prefix .. string.sub(line, nonindent_start) .. suffix
|
||||
end
|
||||
end
|
||||
|
||||
H.make_uncomment_function = function(parts)
|
||||
local l_esc, r_esc = vim.pesc(parts.left), vim.pesc(parts.right)
|
||||
local regex = '^(%s*)' .. l_esc .. '(.*)' .. r_esc .. '(%s-)$'
|
||||
local regex_trimmed = '^(%s*)' .. vim.trim(l_esc) .. '(.*)' .. vim.trim(r_esc) .. '(%s-)$'
|
||||
|
||||
return function(line)
|
||||
-- Try regex with exact comment parts first, fall back to trimmed parts
|
||||
local indent, new_line, trail = line:match(regex)
|
||||
if new_line == nil then
|
||||
indent, new_line, trail = line:match(regex_trimmed)
|
||||
end
|
||||
|
||||
-- Return original if line is not commented
|
||||
if new_line == nil then return line end
|
||||
|
||||
-- Prevent trailing whitespace
|
||||
if H.is_blank(new_line) then
|
||||
indent, trail = '', ''
|
||||
end
|
||||
|
||||
return indent .. new_line .. trail
|
||||
end
|
||||
end
|
||||
|
||||
-- Utilities ------------------------------------------------------------------
|
||||
H.error = function(msg) error('(mini.comment) ' .. msg, 0) end
|
||||
|
||||
H.map = function(mode, lhs, rhs, opts)
|
||||
if lhs == '' then return end
|
||||
opts = vim.tbl_deep_extend('force', { silent = true }, opts or {})
|
||||
vim.keymap.set(mode, lhs, rhs, opts)
|
||||
end
|
||||
|
||||
H.is_blank = function(x) return string.find(x, '^%s*$') ~= nil end
|
||||
|
||||
return MiniComment
|
||||
1417
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/completion.lua
Normal file
1417
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/completion.lua
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,299 @@
|
||||
--- *mini.cursorword* Autohighlight word under cursor
|
||||
--- *MiniCursorword*
|
||||
---
|
||||
--- MIT License Copyright (c) 2021 Evgeni Chasnovski
|
||||
---
|
||||
--- ==============================================================================
|
||||
---
|
||||
--- Features:
|
||||
--- - Autohighlight word under cursor with customizable delay.
|
||||
---
|
||||
--- - Current word under cursor can be highlighted differently.
|
||||
---
|
||||
--- - Highlighting is triggered only if current cursor character is a |[:keyword:]|.
|
||||
---
|
||||
--- - Highlighting stops in insert and terminal modes.
|
||||
---
|
||||
--- - "Word under cursor" is meant as in Vim's |<cword>|: something user would
|
||||
--- get as 'iw' text object.
|
||||
---
|
||||
--- # Setup ~
|
||||
---
|
||||
--- This module needs a setup with `require('mini.cursorword').setup({})`
|
||||
--- (replace `{}` with your `config` table). It will create global Lua table
|
||||
--- `MiniCursorword` which you can use for scripting or manually (with
|
||||
--- `:lua MiniCursorword.*`).
|
||||
---
|
||||
--- See |MiniCursorword.config| for `config` structure and default values.
|
||||
---
|
||||
--- You can override runtime config settings locally to buffer inside
|
||||
--- `vim.b.minicursorword_config` which should have same structure as
|
||||
--- `MiniCursorword.config`. See |mini.nvim-buffer-local-config| for more details.
|
||||
---
|
||||
--- # Highlight groups ~
|
||||
---
|
||||
--- * `MiniCursorword` - highlight group of a non-current cursor word.
|
||||
--- Default: plain underline.
|
||||
---
|
||||
--- * `MiniCursorwordCurrent` - highlight group of a current word under cursor.
|
||||
--- Default: links to `MiniCursorword` (so `:hi clear MiniCursorwordCurrent`
|
||||
--- will lead to showing `MiniCursorword` highlight group).
|
||||
--- Note: To not highlight it, use the following Lua code: >lua
|
||||
---
|
||||
--- vim.api.nvim_set_hl(0, 'MiniCursorwordCurrent', {})
|
||||
--- <
|
||||
--- To change any highlight group, modify it directly with |:highlight|.
|
||||
---
|
||||
--- # Disabling ~
|
||||
---
|
||||
--- To disable core functionality, set `vim.g.minicursorword_disable` (globally) or
|
||||
--- `vim.b.minicursorword_disable` (for a buffer) to `true`. Considering high
|
||||
--- number of different scenarios and customization intentions, writing exact
|
||||
--- rules for disabling module's functionality is left to user. See
|
||||
--- |mini.nvim-disabling-recipes| for common recipes. Note: after disabling
|
||||
--- there might be highlighting left; it will be removed after next
|
||||
--- highlighting update.
|
||||
---
|
||||
--- Module-specific disabling:
|
||||
--- - Don't show highlighting if cursor is on the word that is in a blocklist
|
||||
--- of current filetype. In this example, blocklist for "lua" is "local" and
|
||||
--- "require" words, for "javascript" - "import": >lua
|
||||
---
|
||||
--- _G.cursorword_blocklist = function()
|
||||
--- local curword = vim.fn.expand('<cword>')
|
||||
--- local filetype = vim.bo.filetype
|
||||
---
|
||||
--- -- Add any disabling global or filetype-specific logic here
|
||||
--- local blocklist = {}
|
||||
--- if filetype == 'lua' then
|
||||
--- blocklist = { 'local', 'require' }
|
||||
--- elseif filetype == 'javascript' then
|
||||
--- blocklist = { 'import' }
|
||||
--- end
|
||||
---
|
||||
--- vim.b.minicursorword_disable = vim.tbl_contains(blocklist, curword)
|
||||
--- end
|
||||
---
|
||||
--- -- Make sure to add this autocommand *before* calling module's `setup()`.
|
||||
--- vim.cmd('au CursorMoved * lua _G.cursorword_blocklist()')
|
||||
--- <
|
||||
|
||||
-- Module definition ==========================================================
|
||||
local MiniCursorword = {}
|
||||
local H = {}
|
||||
|
||||
--- Module setup
|
||||
---
|
||||
---@param config table|nil Module config table. See |MiniCursorword.config|.
|
||||
---
|
||||
---@usage >lua
|
||||
--- require('mini.cursorword').setup() -- use default config
|
||||
--- -- OR
|
||||
--- require('mini.cursorword').setup({}) -- replace {} with your config table
|
||||
--- <
|
||||
MiniCursorword.setup = function(config)
|
||||
-- Export module
|
||||
_G.MiniCursorword = MiniCursorword
|
||||
|
||||
-- Setup config
|
||||
config = H.setup_config(config)
|
||||
|
||||
-- Apply config
|
||||
H.apply_config(config)
|
||||
|
||||
-- Define behavior
|
||||
H.create_autocommands()
|
||||
|
||||
-- Create default highlighting
|
||||
H.create_default_hl()
|
||||
end
|
||||
|
||||
--- Module config
|
||||
---
|
||||
--- Default values:
|
||||
---@eval return MiniDoc.afterlines_to_code(MiniDoc.current.eval_section)
|
||||
MiniCursorword.config = {
|
||||
-- Delay (in ms) between when cursor moved and when highlighting appeared
|
||||
delay = 100,
|
||||
}
|
||||
--minidoc_afterlines_end
|
||||
|
||||
-- Module functionality =======================================================
|
||||
|
||||
-- Helper data ================================================================
|
||||
-- Module default config
|
||||
H.default_config = vim.deepcopy(MiniCursorword.config)
|
||||
|
||||
-- Delay timer
|
||||
H.timer = vim.loop.new_timer()
|
||||
|
||||
-- Information about last match highlighting (stored *per window*):
|
||||
-- - Key: windows' unique buffer identifiers.
|
||||
-- - Value: table with:
|
||||
-- - `id` field for match id (from `vim.fn.matchadd()`).
|
||||
-- - `word` field for matched word.
|
||||
H.window_matches = {}
|
||||
|
||||
-- Helper functionality =======================================================
|
||||
-- Settings -------------------------------------------------------------------
|
||||
H.setup_config = function(config)
|
||||
-- General idea: if some table elements are not present in user-supplied
|
||||
-- `config`, take them from default config
|
||||
vim.validate({ config = { config, 'table', true } })
|
||||
config = vim.tbl_deep_extend('force', vim.deepcopy(H.default_config), config or {})
|
||||
|
||||
vim.validate({ delay = { config.delay, 'number' } })
|
||||
|
||||
return config
|
||||
end
|
||||
|
||||
H.apply_config = function(config)
|
||||
MiniCursorword.config = config
|
||||
|
||||
-- Make `setup()` to proper reset module
|
||||
for _, m in ipairs(vim.fn.getmatches()) do
|
||||
if vim.startswith(m.group, 'MiniCursorword') then vim.fn.matchdelete(m.id) end
|
||||
end
|
||||
end
|
||||
|
||||
H.create_autocommands = function()
|
||||
local augroup = vim.api.nvim_create_augroup('MiniCursorword', {})
|
||||
|
||||
local au = function(event, pattern, callback, desc)
|
||||
vim.api.nvim_create_autocmd(event, { group = augroup, pattern = pattern, callback = callback, desc = desc })
|
||||
end
|
||||
|
||||
au('CursorMoved', '*', H.auto_highlight, 'Auto highlight cursorword')
|
||||
au({ 'InsertEnter', 'TermEnter', 'QuitPre' }, '*', H.auto_unhighlight, 'Auto unhighlight cursorword')
|
||||
au('ModeChanged', '*:[^i]', H.auto_highlight, 'Auto highlight cursorword')
|
||||
|
||||
au('ColorScheme', '*', H.create_default_hl, 'Ensure proper colors')
|
||||
au('FileType', 'TelescopePrompt', function() vim.b.minicursorword_disable = true end, 'Disable locally')
|
||||
end
|
||||
|
||||
--stylua: ignore
|
||||
H.create_default_hl = function()
|
||||
vim.api.nvim_set_hl(0, 'MiniCursorword', { default = true, underline = true })
|
||||
vim.api.nvim_set_hl(0, 'MiniCursorwordCurrent', { default = true, link = 'MiniCursorword' })
|
||||
end
|
||||
|
||||
H.is_disabled = function() return vim.g.minicursorword_disable == true or vim.b.minicursorword_disable == true end
|
||||
|
||||
H.get_config = function(config)
|
||||
return vim.tbl_deep_extend('force', MiniCursorword.config, vim.b.minicursorword_config or {}, config or {})
|
||||
end
|
||||
|
||||
-- Autocommands ---------------------------------------------------------------
|
||||
H.auto_highlight = function()
|
||||
-- Stop any possible previous delayed highlighting
|
||||
H.timer:stop()
|
||||
|
||||
-- Stop highlighting immediately if module is disabled when cursor is not on
|
||||
-- 'keyword'
|
||||
if not H.should_highlight() then return H.unhighlight() end
|
||||
|
||||
-- Get current information
|
||||
local win_id = vim.api.nvim_get_current_win()
|
||||
local win_match = H.window_matches[win_id] or {}
|
||||
local curword = H.get_cursor_word()
|
||||
|
||||
-- Only immediately update highlighting of current word under cursor if
|
||||
-- currently highlighted word equals one under cursor
|
||||
if win_match.word == curword then
|
||||
H.unhighlight(true)
|
||||
H.highlight(true)
|
||||
return
|
||||
end
|
||||
|
||||
-- Stop highlighting previous match (if it exists)
|
||||
H.unhighlight()
|
||||
|
||||
-- Delay highlighting
|
||||
H.timer:start(
|
||||
H.get_config().delay,
|
||||
0,
|
||||
vim.schedule_wrap(function()
|
||||
-- Ensure that always only one word is highlighted
|
||||
H.unhighlight()
|
||||
H.highlight()
|
||||
end)
|
||||
)
|
||||
end
|
||||
|
||||
H.auto_unhighlight = function()
|
||||
-- Stop any possible previous delayed highlighting
|
||||
H.timer:stop()
|
||||
H.unhighlight()
|
||||
end
|
||||
|
||||
-- Highlighting ---------------------------------------------------------------
|
||||
---@param only_current boolean|nil Whether to forcefully highlight only current word
|
||||
--- under cursor.
|
||||
---@private
|
||||
H.highlight = function(only_current)
|
||||
-- A modified version of https://stackoverflow.com/a/25233145
|
||||
-- Using `matchadd()` instead of a simpler `:match` to tweak priority of
|
||||
-- 'current word' highlighting: with `:match` it is higher than for
|
||||
-- `incsearch` which is not convenient.
|
||||
local win_id = vim.api.nvim_get_current_win()
|
||||
if not vim.api.nvim_win_is_valid(win_id) then return end
|
||||
|
||||
if not H.should_highlight() then return end
|
||||
|
||||
H.window_matches[win_id] = H.window_matches[win_id] or {}
|
||||
|
||||
-- Add match highlight for current word under cursor
|
||||
local current_word_pattern = [[\k*\%#\k*]]
|
||||
local match_id_current = vim.fn.matchadd('MiniCursorwordCurrent', current_word_pattern, -1)
|
||||
H.window_matches[win_id].id_current = match_id_current
|
||||
|
||||
-- Don't add main match id if not needed or if one is already present
|
||||
if only_current or H.window_matches[win_id].id ~= nil then return end
|
||||
|
||||
-- Add match highlight for non-current word under cursor. NOTEs:
|
||||
-- - Using `\(...\)\@!` allows to not match current word.
|
||||
-- - Using 'very nomagic' ('\V') allows not escaping.
|
||||
-- - Using `\<` and `\>` matches whole word (and not as part).
|
||||
local curword = H.get_cursor_word()
|
||||
local pattern = string.format([[\(%s\)\@!\&\V\<%s\>]], current_word_pattern, curword)
|
||||
local match_id = vim.fn.matchadd('MiniCursorword', pattern, -1)
|
||||
|
||||
-- Store information about highlight
|
||||
H.window_matches[win_id].id = match_id
|
||||
H.window_matches[win_id].word = curword
|
||||
end
|
||||
|
||||
---@param only_current boolean|nil Whether to remove highlighting only of current
|
||||
--- word under cursor.
|
||||
---@private
|
||||
H.unhighlight = function(only_current)
|
||||
-- Don't do anything if there is no valid information to act upon
|
||||
local win_id = vim.api.nvim_get_current_win()
|
||||
local win_match = H.window_matches[win_id]
|
||||
if not vim.api.nvim_win_is_valid(win_id) or win_match == nil then return end
|
||||
|
||||
-- Use `pcall` because there is an error if match id is not present. It can
|
||||
-- happen if something else called `clearmatches`.
|
||||
pcall(vim.fn.matchdelete, win_match.id_current)
|
||||
H.window_matches[win_id].id_current = nil
|
||||
|
||||
if not only_current then
|
||||
pcall(vim.fn.matchdelete, win_match.id)
|
||||
H.window_matches[win_id] = nil
|
||||
end
|
||||
end
|
||||
|
||||
H.should_highlight = function() return not H.is_disabled() and H.is_cursor_on_keyword() end
|
||||
|
||||
H.is_cursor_on_keyword = function()
|
||||
local col = vim.fn.col('.')
|
||||
local curchar = vim.api.nvim_get_current_line():sub(col, col)
|
||||
|
||||
-- Use `pcall()` to catch `E5108` (can happen in binary files, see #112)
|
||||
local ok, match_res = pcall(vim.fn.match, curchar, '[[:keyword:]]')
|
||||
return ok and match_res >= 0
|
||||
end
|
||||
|
||||
H.get_cursor_word = function() return vim.fn.escape(vim.fn.expand('<cword>'), [[\/]]) end
|
||||
|
||||
return MiniCursorword
|
||||
1616
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/deps.lua
Normal file
1616
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/deps.lua
Normal file
File diff suppressed because it is too large
Load Diff
1806
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/diff.lua
Normal file
1806
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/diff.lua
Normal file
File diff suppressed because it is too large
Load Diff
1326
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/doc.lua
Normal file
1326
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/doc.lua
Normal file
File diff suppressed because it is too large
Load Diff
2055
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/extra.lua
Normal file
2055
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/extra.lua
Normal file
File diff suppressed because it is too large
Load Diff
2641
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/files.lua
Normal file
2641
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/files.lua
Normal file
File diff suppressed because it is too large
Load Diff
376
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/fuzzy.lua
Normal file
376
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/fuzzy.lua
Normal file
@ -0,0 +1,376 @@
|
||||
--- *mini.fuzzy* Fuzzy matching
|
||||
--- *MiniFuzzy*
|
||||
---
|
||||
--- MIT License Copyright (c) 2021 Evgeni Chasnovski
|
||||
---
|
||||
--- ==============================================================================
|
||||
---
|
||||
--- Features:
|
||||
--- - Minimal and fast fuzzy matching algorithm which prioritizes match width.
|
||||
---
|
||||
--- - Functions to for common fuzzy matching operations:
|
||||
--- - |MiniFuzzy.match()|.
|
||||
--- - |MiniFuzzy.filtersort()|.
|
||||
--- - |MiniFuzzy.process_lsp_items()|.
|
||||
---
|
||||
--- - Generator of |telescope.nvim| sorter: |MiniFuzzy.get_telescope_sorter()|.
|
||||
---
|
||||
--- # Setup ~
|
||||
---
|
||||
--- This module doesn't need setup, but it can be done to improve usability.
|
||||
--- Setup with `require('mini.fuzzy').setup({})` (replace `{}` with your
|
||||
--- `config` table). It will create global Lua table `MiniFuzzy` which you can
|
||||
--- use for scripting or manually (with `:lua MiniFuzzy.*`).
|
||||
---
|
||||
--- See |MiniFuzzy.config| for `config` structure and default values.
|
||||
---
|
||||
--- You can override runtime config settings locally to buffer inside
|
||||
--- `vim.b.minifuzzy_config` which should have same structure as
|
||||
--- `MiniFuzzy.config`.
|
||||
--- See |mini.nvim-buffer-local-config| for more details.
|
||||
---
|
||||
--- # Notes ~
|
||||
---
|
||||
--- 1. Currently there is no explicit design to work with multibyte symbols,
|
||||
--- but simple examples should work.
|
||||
--- 2. Smart case is used: case insensitive if input word (which is usually a
|
||||
--- user input) is all lower case. Case sensitive otherwise.
|
||||
|
||||
--- # Algorithm design ~
|
||||
---
|
||||
--- General design uses only width of found match and index of first letter
|
||||
--- match. No special characters or positions (like in fzy and fzf) are used.
|
||||
---
|
||||
--- Given input `word` and target `candidate`:
|
||||
--- - The goal is to find matching between `word`'s letters and letters in
|
||||
--- `candidate`, which minimizes certain score. It is assumed that order of
|
||||
--- letters in `word` and those matched in `candidate` should be the same.
|
||||
--- - Matching is represented by matched positions: an array `positions` of
|
||||
--- integers with length equal to number of letters in `word`. The following
|
||||
--- should be always true in case of a match: `candidate`'s letter at index
|
||||
--- `positions[i]` is letters[i]` for all valid `i`.
|
||||
--- - Matched positions are evaluated based only on two features: their width
|
||||
--- (number of indexes between first and last positions) and first match
|
||||
--- (index of first letter match). There is a global setting `cutoff` for
|
||||
--- which all feature values greater than it can be considered "equally bad".
|
||||
--- - Score of matched positions is computed with following explicit formula:
|
||||
--- `cutoff * min(width, cutoff) + min(first, cutoff)`. It is designed to be
|
||||
--- equivalent to first comparing widths (lower is better) and then comparing
|
||||
--- first match (lower is better). For example, if `word = 'time'`:
|
||||
--- - '_time' (width 4) will have a better match than 't_ime' (width 5).
|
||||
--- - 'time_a' (width 4, first 1) will have a better match than 'a_time'
|
||||
--- (width 4, first 3).
|
||||
--- - Final matched positions are those which minimize score among all possible
|
||||
--- matched positions of `word` and `candidate`.
|
||||
---@tag MiniFuzzy-algorithm
|
||||
|
||||
-- Module definition ==========================================================
|
||||
local MiniFuzzy = {}
|
||||
local H = {}
|
||||
|
||||
--- Module setup
|
||||
---
|
||||
---@param config table|nil Module config table. See |MiniFuzzy.config|.
|
||||
---
|
||||
---@usage >lua
|
||||
--- require('mini.fuzzy').setup() -- use default config
|
||||
--- -- OR
|
||||
--- require('mini.fuzzy').setup({}) -- replace {} with your config table
|
||||
--- <
|
||||
MiniFuzzy.setup = function(config)
|
||||
-- Export module
|
||||
_G.MiniFuzzy = MiniFuzzy
|
||||
|
||||
-- Setup config
|
||||
config = H.setup_config(config)
|
||||
|
||||
-- Apply config
|
||||
H.apply_config(config)
|
||||
end
|
||||
|
||||
--- Module config
|
||||
---
|
||||
--- Default values:
|
||||
---@eval return MiniDoc.afterlines_to_code(MiniDoc.current.eval_section)
|
||||
MiniFuzzy.config = {
|
||||
-- Maximum allowed value of match features (width and first match). All
|
||||
-- feature values greater than cutoff can be considered "equally bad".
|
||||
cutoff = 100,
|
||||
}
|
||||
--minidoc_afterlines_end
|
||||
|
||||
-- Module functionality =======================================================
|
||||
--- Compute match data of input `word` and `candidate` strings
|
||||
---
|
||||
--- It tries to find best match for input string `word` (usually user input)
|
||||
--- and string `candidate`. Returns table with elements:
|
||||
--- - `positions` - array with letter indexes inside `candidate` which
|
||||
--- matched to corresponding letters in `word`. Or `nil` if no match.
|
||||
--- - `score` - positive number representing how good the match is (lower is
|
||||
--- better). Or `-1` if no match.
|
||||
---
|
||||
---@param word string Input word (usually user input).
|
||||
---@param candidate string Target word (usually with which matching is done).
|
||||
---
|
||||
---@return table Table with matching information (see function's description).
|
||||
MiniFuzzy.match = function(word, candidate)
|
||||
-- Use 'smart case'
|
||||
candidate = (word == word:lower()) and candidate:lower() or candidate
|
||||
|
||||
local positions = H.find_best_positions(H.string_to_letters(word), candidate)
|
||||
return { positions = positions, score = H.score_positions(positions) }
|
||||
end
|
||||
|
||||
--- Filter string array
|
||||
---
|
||||
--- This leaves only those elements of input array which matched with `word`
|
||||
--- and sorts from best to worst matches (based on score and index in original
|
||||
--- array, both lower is better).
|
||||
---
|
||||
---@param word string String which will be searched.
|
||||
---@param candidate_array table Lua array of strings inside which word will be
|
||||
--- searched.
|
||||
---
|
||||
---@return ... Arrays of matched candidates and their indexes in original input.
|
||||
MiniFuzzy.filtersort = function(word, candidate_array)
|
||||
-- Use 'smart case'. Create new array to preserve input for later filtering
|
||||
local cand_array
|
||||
if word == word:lower() then
|
||||
cand_array = vim.tbl_map(string.lower, candidate_array)
|
||||
else
|
||||
cand_array = candidate_array
|
||||
end
|
||||
|
||||
local filter_ids = H.make_filter_indexes(word, cand_array)
|
||||
table.sort(filter_ids, H.compare_filter_indexes)
|
||||
|
||||
return H.filter_by_indexes(candidate_array, filter_ids)
|
||||
end
|
||||
|
||||
--- Fuzzy matching for `lsp_completion.process_items` of |MiniCompletion.config|
|
||||
---
|
||||
---@param items table Array with LSP 'textDocument/completion' response items.
|
||||
---@param base string Word to complete.
|
||||
MiniFuzzy.process_lsp_items = function(items, base)
|
||||
-- Extract completion words from items
|
||||
local words = vim.tbl_map(function(x)
|
||||
if type(x.textEdit) == 'table' and type(x.textEdit.newText) == 'string' then return x.textEdit.newText end
|
||||
if type(x.insertText) == 'string' then return x.insertText end
|
||||
if type(x.label) == 'string' then return x.label end
|
||||
return ''
|
||||
end, items)
|
||||
|
||||
-- Fuzzy match
|
||||
local _, match_inds = MiniFuzzy.filtersort(base, words)
|
||||
return vim.tbl_map(function(i) return items[i] end, match_inds)
|
||||
end
|
||||
|
||||
--- Custom getter for `telescope.nvim` sorter
|
||||
---
|
||||
--- Designed to be used as value for |telescope.defaults.file_sorter| and
|
||||
--- |telescope.defaults.generic_sorter| inside `setup()` call.
|
||||
---
|
||||
---@param opts table|nil Options (currently not used).
|
||||
---
|
||||
---@usage >lua
|
||||
--- require('telescope').setup({
|
||||
--- defaults = {
|
||||
--- generic_sorter = require('mini.fuzzy').get_telescope_sorter
|
||||
--- }
|
||||
--- })
|
||||
--- <
|
||||
MiniFuzzy.get_telescope_sorter = function(opts)
|
||||
opts = opts or {}
|
||||
|
||||
return require('telescope.sorters').Sorter:new({
|
||||
start = function(self, prompt)
|
||||
-- Cache prompt's letters
|
||||
self.letters = H.string_to_letters(prompt)
|
||||
|
||||
-- Use 'smart case': insensitive if `prompt` is lowercase
|
||||
self.case_sensitive = prompt ~= prompt:lower()
|
||||
end,
|
||||
|
||||
-- @param self
|
||||
-- @param prompt (which is the text on the line)
|
||||
-- @param line (entry.ordinal)
|
||||
-- @param entry (the whole entry)
|
||||
scoring_function = function(self, _, line, _)
|
||||
if #self.letters == 0 then return 1 end
|
||||
line = self.case_sensitive and line or line:lower()
|
||||
local positions = H.find_best_positions(self.letters, line)
|
||||
return H.score_positions(positions)
|
||||
end,
|
||||
|
||||
-- Currently there doesn't seem to be a proper way to cache matched
|
||||
-- positions from inside of `scoring_function` (see `highlighter` code of
|
||||
-- `get_fzy_sorter`'s output). Besides, it seems that `display` and `line`
|
||||
-- arguments might be different. So, extra calls to `match` are made.
|
||||
highlighter = function(self, _, display)
|
||||
if #self.letters == 0 or #display == 0 then return {} end
|
||||
display = self.case_sensitive and display or display:lower()
|
||||
return H.find_best_positions(self.letters, display)
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
-- Helper data ================================================================
|
||||
-- Module default config
|
||||
H.default_config = vim.deepcopy(MiniFuzzy.config)
|
||||
|
||||
-- Helper functionality =======================================================
|
||||
-- Settings -------------------------------------------------------------------
|
||||
H.setup_config = function(config)
|
||||
-- General idea: if some table elements are not present in user-supplied
|
||||
-- `config`, take them from default config
|
||||
vim.validate({ config = { config, 'table', true } })
|
||||
config = vim.tbl_deep_extend('force', vim.deepcopy(H.default_config), config or {})
|
||||
|
||||
vim.validate({
|
||||
cutoff = {
|
||||
config.cutoff,
|
||||
function(x) return type(x) == 'number' and x >= 1 end,
|
||||
'number not less than 1',
|
||||
},
|
||||
})
|
||||
|
||||
return config
|
||||
end
|
||||
|
||||
H.apply_config = function(config) MiniFuzzy.config = config end
|
||||
|
||||
H.is_disabled = function() return vim.g.minifuzzy_disable == true or vim.b.minifuzzy_disable == true end
|
||||
|
||||
H.get_config = function(config)
|
||||
return vim.tbl_deep_extend('force', MiniFuzzy.config, vim.b.minifuzzy_config or {}, config or {})
|
||||
end
|
||||
|
||||
-- Fuzzy matching -------------------------------------------------------------
|
||||
---@param letters table Array of letters from input word
|
||||
---@param candidate string String of interest
|
||||
---
|
||||
---@return table|nil Table with matched positions (in `candidate`) if there is a
|
||||
--- match, `nil` otherwise.
|
||||
---@private
|
||||
H.find_best_positions = function(letters, candidate)
|
||||
local n_candidate, n_letters = #candidate, #letters
|
||||
if n_letters == 0 or n_candidate < n_letters then return nil end
|
||||
|
||||
-- Search forward to find matching positions with left-most last letter match
|
||||
local pos_last = 0
|
||||
for let_i = 1, #letters do
|
||||
pos_last = candidate:find(letters[let_i], pos_last + 1)
|
||||
if not pos_last then break end
|
||||
end
|
||||
|
||||
-- Candidate is matched only if word's last letter is found
|
||||
if not pos_last then return nil end
|
||||
|
||||
-- If there is only one letter, it is already the best match (there will not
|
||||
-- be better width and it has lowest first match)
|
||||
if n_letters == 1 then return { pos_last } end
|
||||
|
||||
-- Compute best match positions by iteratively checking all possible last
|
||||
-- letter matches (at and after initial one). At end of each iteration
|
||||
-- `best_pos_last` holds best match for last letter among all previously
|
||||
-- checked such matches.
|
||||
local best_pos_last, best_width = pos_last, math.huge
|
||||
local rev_candidate = candidate:reverse()
|
||||
|
||||
local cutoff = H.get_config().cutoff
|
||||
while pos_last do
|
||||
-- Simulate computing best match positions ending exactly at `pos_last` by
|
||||
-- going backwards from current last letter match. This works because it
|
||||
-- minimizes width which is the only way to find match with lower score.
|
||||
-- Not actually creating table with positions and then directly computing
|
||||
-- score increases speed by up to 40% (on small frequent input word with
|
||||
-- relatively wide candidate, such as file paths of nested directories).
|
||||
local rev_first = n_candidate - pos_last + 1
|
||||
for i = #letters - 1, 1, -1 do
|
||||
rev_first = rev_candidate:find(letters[i], rev_first + 1)
|
||||
end
|
||||
local first = n_candidate - rev_first + 1
|
||||
local width = math.min(pos_last - first + 1, cutoff)
|
||||
|
||||
-- Using strict sign is crucial because when two last letter matches result
|
||||
-- into positions with similar width, the one which was created earlier
|
||||
-- (i.e. with smaller last letter match) will have smaller first letter
|
||||
-- match (hence better score).
|
||||
if width < best_width then
|
||||
best_pos_last, best_width = pos_last, width
|
||||
end
|
||||
|
||||
-- Advance iteration
|
||||
pos_last = candidate:find(letters[n_letters], pos_last + 1)
|
||||
end
|
||||
|
||||
-- Actually compute best matched positions from best last letter match
|
||||
local best_positions = { best_pos_last }
|
||||
local rev_pos = n_candidate - best_pos_last + 1
|
||||
for i = #letters - 1, 1, -1 do
|
||||
rev_pos = rev_candidate:find(letters[i], rev_pos + 1)
|
||||
-- For relatively small number of letters (around 10, which is main use
|
||||
-- case) inserting to front seems to have better performance than
|
||||
-- inserting at end and then reversing.
|
||||
table.insert(best_positions, 1, n_candidate - rev_pos + 1)
|
||||
end
|
||||
|
||||
return best_positions
|
||||
end
|
||||
|
||||
-- Compute score of matched positions. Smaller values indicate better match
|
||||
-- (i.e. like distance). Reasoning behind the score is for it to produce the
|
||||
-- same ordering as with sequential comparison of match's width and first
|
||||
-- position. So it shouldn't really be perceived as linear distance (difference
|
||||
-- between scores don't really matter, only their comparison with each other).
|
||||
--
|
||||
-- Reasoning behind comparison logic (based on 'time' input):
|
||||
-- - '_time' is better than 't_ime' (width is smaller).
|
||||
-- - 'time_aa' is better than 'aa_time' (same width, first match is smaller).
|
||||
--
|
||||
-- Returns -1 if `positions` is `nil` or empty.
|
||||
H.score_positions = function(positions)
|
||||
if not positions or #positions == 0 then return -1 end
|
||||
local first, last = positions[1], positions[#positions]
|
||||
local cutoff = H.get_config().cutoff
|
||||
return cutoff * math.min(last - first + 1, cutoff) + math.min(first, cutoff)
|
||||
end
|
||||
|
||||
H.make_filter_indexes = function(word, candidate_array)
|
||||
-- Precompute a table of word's letters
|
||||
local letters = H.string_to_letters(word)
|
||||
|
||||
local res = {}
|
||||
for i, cand in ipairs(candidate_array) do
|
||||
local positions = H.find_best_positions(letters, cand)
|
||||
if positions then table.insert(res, { index = i, score = H.score_positions(positions) }) end
|
||||
end
|
||||
|
||||
return res
|
||||
end
|
||||
|
||||
H.compare_filter_indexes = function(a, b)
|
||||
if a.score < b.score then return true end
|
||||
|
||||
if a.score == b.score then
|
||||
-- Make sorting stable by preserving index order
|
||||
return a.index < b.index
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
H.filter_by_indexes = function(candidate_array, ids)
|
||||
local res, res_ids = {}, {}
|
||||
for _, id in pairs(ids) do
|
||||
table.insert(res, candidate_array[id.index])
|
||||
table.insert(res_ids, id.index)
|
||||
end
|
||||
|
||||
return res, res_ids
|
||||
end
|
||||
|
||||
-- Utilities ------------------------------------------------------------------
|
||||
H.string_to_letters = function(s) return vim.tbl_map(vim.pesc, vim.split(s, '')) end
|
||||
|
||||
return MiniFuzzy
|
||||
1674
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/git.lua
Normal file
1674
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/git.lua
Normal file
File diff suppressed because it is too large
Load Diff
1031
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/hipatterns.lua
Normal file
1031
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/hipatterns.lua
Normal file
File diff suppressed because it is too large
Load Diff
1819
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/hues.lua
Normal file
1819
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/hues.lua
Normal file
File diff suppressed because it is too large
Load Diff
2116
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/icons.lua
Normal file
2116
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/icons.lua
Normal file
File diff suppressed because it is too large
Load Diff
1122
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/indentscope.lua
Normal file
1122
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/indentscope.lua
Normal file
File diff suppressed because it is too large
Load Diff
386
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/init.lua
Normal file
386
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/init.lua
Normal file
@ -0,0 +1,386 @@
|
||||
--- *mini.nvim* Collection of minimal, independent and fast Lua modules
|
||||
---
|
||||
--- MIT License Copyright (c) 2021 Evgeni Chasnovski
|
||||
---
|
||||
--- ==============================================================================
|
||||
---
|
||||
--- |mini.nvim| is a collection of minimal, independent, and fast Lua modules
|
||||
--- dedicated to improve Neovim (version 0.7 and higher) experience. Each
|
||||
--- module can be considered as a separate sub-plugin.
|
||||
---
|
||||
--- Table of contents:
|
||||
--- General overview ............................................... |mini.nvim|
|
||||
--- Disabling recepies ........................... |mini.nvim-disabling-recipes|
|
||||
--- Buffer-local config ........................ |mini.nvim-buffer-local-config|
|
||||
--- Plugin colorschemes ................................... |mini-color-schemes|
|
||||
--- Extend and create a/i textobjects ................................ |mini.ai|
|
||||
--- Align text interactively ...................................... |mini.align|
|
||||
--- Animate common Neovim actions ............................... |mini.animate|
|
||||
--- Base16 colorscheme creation .................................. |mini.base16|
|
||||
--- Common configuration presets ................................. |mini.basics|
|
||||
--- Go forward/backward with square brackets .................. |mini.bracketed|
|
||||
--- Remove buffers ............................................ |mini.bufremove|
|
||||
--- Show next key clues ............................................ |mini.clue|
|
||||
--- Tweak and save any color scheme .............................. |mini.colors|
|
||||
--- Comment lines ............................................... |mini.comment|
|
||||
--- Completion and signature help ............................ |mini.completion|
|
||||
--- Autohighlight word under cursor .......................... |mini.cursorword|
|
||||
--- Plugin manager ................................................. |mini.deps|
|
||||
--- Work with diff hunks ........................................... |mini.diff|
|
||||
--- Generate Neovim help files ...................................... |mini.doc|
|
||||
--- Extra 'mini.nvim' functionality ............................... |mini.extra|
|
||||
--- Navigate and manipulate file system............................ |mini.files|
|
||||
--- Fuzzy matching ................................................ |mini.fuzzy|
|
||||
--- Git integration ................................................. |mini.git|
|
||||
--- Highlight patterns in text ............................... |mini.hipatterns|
|
||||
--- Generate configurable color scheme ............................. |mini.hues|
|
||||
--- Icon provider ................................................. |mini.icons|
|
||||
--- Visualize and work with indent scope .................... |mini.indentscope|
|
||||
--- Jump to next/previous single character ......................... |mini.jump|
|
||||
--- Jump within visible lines .................................... |mini.jump2d|
|
||||
--- Window with buffer text overview ................................ |mini.map|
|
||||
--- Miscellaneous functions ........................................ |mini.misc|
|
||||
--- Move any selection in any direction ............................ |mini.move|
|
||||
--- Show notifications ........................................... |mini.notify|
|
||||
--- Text edit operators ....................................... |mini.operators|
|
||||
--- Autopairs ..................................................... |mini.pairs|
|
||||
--- Pick anything .................................................. |mini.pick|
|
||||
--- Session management ......................................... |mini.sessions|
|
||||
--- Split and join arguments .................................. |mini.splitjoin|
|
||||
--- Start screen ................................................ |mini.starter|
|
||||
--- Statusline ............................................... |mini.statusline|
|
||||
--- Surround actions ........................................... |mini.surround|
|
||||
--- Tabline ..................................................... |mini.tabline|
|
||||
--- Test Neovim plugins ............................................ |mini.test|
|
||||
--- Trailspace (highlight and remove)......................... |mini.trailspace|
|
||||
--- Track and reuse file system visits ........................... |mini.visits|
|
||||
---
|
||||
--- # General principles ~
|
||||
---
|
||||
--- - <Design>. Each module is designed to solve a particular problem targeting
|
||||
--- balance between feature-richness (handling as many edge-cases as
|
||||
--- possible) and simplicity of implementation/support. Granted, not all of
|
||||
--- them ended up with the same balance, but it is the goal nevertheless.
|
||||
---
|
||||
--- - <Independence>. Modules are independent of each other and can be run
|
||||
--- without external dependencies. Although some of them may need
|
||||
--- dependencies for full experience.
|
||||
---
|
||||
--- - <Structure>. Each module is a submodule for a placeholder "mini" module. So,
|
||||
--- for example, "surround" module should be referred to as "mini.surround".
|
||||
--- As later will be explained, this plugin can also be referred to
|
||||
--- as "MiniSurround".
|
||||
---
|
||||
--- - <Setup>:
|
||||
--- - Each module you want to use should be enabled separately with
|
||||
--- `require(<name of module>).setup({})`. Possibly replace `{}` with
|
||||
--- your config table or omit altogether to use defaults. You can supply
|
||||
--- only parts of config, the rest will be inferred from defaults.
|
||||
---
|
||||
--- - Call to module's `setup()` always creates a global Lua object with
|
||||
--- coherent camel-case name: `require('mini.surround').setup()` creates
|
||||
--- `_G.MiniSurround`. This allows for a simpler usage of plugin
|
||||
--- functionality: instead of `require('mini.surround')` use
|
||||
--- `MiniSurround` (or manually `:lua MiniSurround.*` in command line);
|
||||
--- available from `v:lua` like `v:lua.MiniSurround`. Considering this,
|
||||
--- "module" and "Lua object" names can be used interchangeably:
|
||||
--- 'mini.surround' and 'MiniSurround' will mean the same thing.
|
||||
---
|
||||
--- - Each supplied `config` table (after extending with default values) is
|
||||
--- stored in `config` field of global object. Like `MiniSurround.config`.
|
||||
---
|
||||
--- - Values of `config`, which affect runtime activity, can be changed on
|
||||
--- the fly to have effect. For example, `MiniSurround.config.n_lines`
|
||||
--- can be changed during runtime; but changing
|
||||
--- `MiniSurround.config.mappings` won't have any effect (as mappings are
|
||||
--- created once during `setup()`).
|
||||
---
|
||||
--- - <Buffer local configuration>. Each module can be additionally configured
|
||||
--- to use certain runtime config settings locally to buffer.
|
||||
--- See |mini.nvim-buffer-local-config| for more information.
|
||||
---
|
||||
--- - <Disabling>. Each module's core functionality can be disabled globally or
|
||||
--- locally to buffer. See "Disabling" section in module's help page for more
|
||||
--- details. See |mini.nvim-disabling-recipes| for common recipes.
|
||||
---
|
||||
--- - <Silencing>. Each module can be configured to not show non-error feedback
|
||||
--- globally or locally to buffer. See "Silencing" section in module's help page
|
||||
--- for more details.
|
||||
---
|
||||
--- - <Highlighting>. Appearance of module's output is controlled by certain set
|
||||
--- of highlight groups (see |highlight-groups|). By default they usually link to
|
||||
--- some semantically close built-in highlight group. Use |:highlight| command
|
||||
--- or |nvim_set_hl()| Lua function to customize highlighting.
|
||||
--- To see a more calibrated look, use |MiniHues|, |MiniBase16|, or plugin's
|
||||
--- colorschemes.
|
||||
---
|
||||
--- - <Stability>. Each module upon release is considered to be relatively
|
||||
--- stable: both in terms of setup and functionality. Any non-bugfix
|
||||
--- backward-incompatible change will be released gradually as much as possible.
|
||||
---
|
||||
--- # List of modules ~
|
||||
---
|
||||
--- - |MiniAi| - extend and create `a`/`i` textobjects (like in `di(` or
|
||||
--- `va"`). It enhances some builtin |text-objects| (like |a(|, |a)|, |a'|,
|
||||
--- and more), creates new ones (like `a*`, `a<Space>`, `af`, `a?`, and
|
||||
--- more), and allows user to create their own (like based on treesitter, and
|
||||
--- more). Supports dot-repeat, `v:count`, different search methods,
|
||||
--- consecutive application, and customization via Lua patterns or functions.
|
||||
--- Has builtins for brackets, quotes, function call, argument, tag, user
|
||||
--- prompt, and any punctuation/digit/whitespace character.
|
||||
---
|
||||
--- - |MiniAlign| - align text interactively (with or without instant preview).
|
||||
--- Allows rich and flexible customization of both alignment rules and user
|
||||
--- interaction. Works with charwise, linewise, and blockwise selections in
|
||||
--- both Normal mode (on textobject/motion; with dot-repeat) and Visual mode.
|
||||
---
|
||||
--- - |MiniAnimate| - animate common Neovim actions. Has "works out of the box"
|
||||
--- builtin animations for cursor movement, scroll, resize, window open and
|
||||
--- close. All of them can be customized and enabled/disabled independently.
|
||||
---
|
||||
--- - |MiniBase16| - fast implementation of base16 theme for manually supplied
|
||||
--- palette. Supports 30+ plugin integrations. Has unique palette generator
|
||||
--- which needs only background and foreground colors.
|
||||
---
|
||||
--- - |MiniBasics| - common configuration presets. Has configurable presets for
|
||||
--- options, mappings, and autocommands. It doesn't change option or mapping
|
||||
--- if it was manually created.
|
||||
---
|
||||
--- - |MiniBracketed| - go forward/backward with square brackets. Among others,
|
||||
--- supports variety of non-trivial targets: comments, files on disk, indent
|
||||
--- changes, tree-sitter nodes, linear undo states, yank history entries.
|
||||
---
|
||||
--- - |MiniBufremove| - buffer removing (unshow, delete, wipeout) while saving
|
||||
--- window layout.
|
||||
---
|
||||
--- - |MiniClue| - show next key clues. Implements custom key query process with
|
||||
--- customizable opt-in triggers, next key descriptions (clues), hydra-like
|
||||
--- submodes, window delay/config. Provides clue sets for some built-in
|
||||
--- concepts: `g`/`z` keys, window commands, etc.
|
||||
---
|
||||
--- - |MiniColors| - tweak and save any color scheme. Can create colorscheme
|
||||
--- object with methods to invert/set/modify/etc.
|
||||
--- lightness/saturation/hue/temperature/etc. of foreground/background/all
|
||||
--- colors, infer cterm attributes, add transparency, save to a file and more.
|
||||
--- Has functionality for interactive experiments and animation of
|
||||
--- transition between color schemes.
|
||||
---
|
||||
--- - |MiniComment| - fast and familiar per-line code commenting.
|
||||
---
|
||||
--- - |MiniCompletion| - async (with customizable 'debounce' delay) 'two-stage
|
||||
--- chain completion': first builtin LSP, then configurable fallback. Also
|
||||
--- has functionality for completion item info and function signature (both
|
||||
--- in floating window appearing after customizable delay).
|
||||
---
|
||||
--- - |MiniCursorword| - automatic highlighting of word under cursor (displayed
|
||||
--- after customizable delay). Current word under cursor can be highlighted
|
||||
--- differently.
|
||||
---
|
||||
--- - |MiniDeps| - plugin manager for plugins outside of 'mini.nvim'. Uses Git and
|
||||
--- built-in packages to install, update, clean, and snapshot plugins.
|
||||
---
|
||||
--- - |MiniDiff| - visualize difference between buffer text and its reference
|
||||
--- interactively (with colored signs or line numbers). Uses Git index as
|
||||
--- default reference. Provides toggleable overview in text area, built-in
|
||||
--- apply/reset/textobject/goto mappings.
|
||||
---
|
||||
--- - |MiniDoc| - generation of help files from EmmyLua-like annotations.
|
||||
--- Allows flexible customization of output via hook functions. Used for
|
||||
--- documenting this plugin.
|
||||
---
|
||||
--- - |MiniExtra| - extra 'mini.nvim' functionality. Contains 'mini.pick' pickers,
|
||||
--- 'mini.ai' textobjects, and more.
|
||||
---
|
||||
--- - |MiniFiles| - navigate and manipulate file system. A file explorer with
|
||||
--- column view capable of manipulating file system by editing text. Can
|
||||
--- create/delete/rename/copy/move files/directories inside and across
|
||||
--- directories. For full experience needs enabled |MiniIcons| module (but works
|
||||
--- without it).
|
||||
---
|
||||
--- - |MiniFuzzy| - functions for fast and simple fuzzy matching. It has
|
||||
--- not only functions to perform fuzzy matching of one string to others, but
|
||||
--- also a sorter for |telescope.nvim|.
|
||||
---
|
||||
--- - |MiniGit| - Git integration (https://git-scm.com/). Implements tracking of
|
||||
--- Git related data (root, branch, etc.), |:Git| command for better integration
|
||||
--- with running Neovim instance, and various helpers to explore Git history.
|
||||
---
|
||||
--- - |MiniHipatterns| - highlight patterns in text with configurable highlighters
|
||||
--- (pattern and/or highlight group can be string or callable).
|
||||
--- Works asynchronously with configurable debounce delay.
|
||||
---
|
||||
--- - |MiniHues| - generate configurable color scheme. Takes only background
|
||||
--- and foreground colors as required arguments. Can adjust number of hues
|
||||
--- for non-base colors, saturation, accent color, plugin integration.
|
||||
---
|
||||
--- - |MiniIcons| - icon provider with fixed set of highlight groups.
|
||||
--- Supports various categories, icon and style customizations, caching for
|
||||
--- performance. Integrates with Neovim's filetype matching.
|
||||
---
|
||||
--- - |MiniIndentscope| - visualize and operate on indent scope. Supports
|
||||
--- customization of debounce delay, animation style, and different
|
||||
--- granularity of options for scope computing algorithm.
|
||||
---
|
||||
--- - |MiniJump| - minimal and fast module for smarter jumping to a single
|
||||
--- character.
|
||||
---
|
||||
--- - |MiniJump2d| - minimal and fast Lua plugin for jumping (moving cursor)
|
||||
--- within visible lines via iterative label filtering. Supports custom jump
|
||||
--- targets (spots), labels, hooks, allowed windows and lines, and more.
|
||||
---
|
||||
--- - |MiniMap| - window with buffer text overview, scrollbar, and highlights.
|
||||
--- Allows configurable symbols for line encode and scrollbar, extensible
|
||||
--- highlight integration (with pre-built ones for builtin search, diagnostic,
|
||||
--- git line status), window properties, and more.
|
||||
---
|
||||
--- - |MiniMisc| - collection of miscellaneous useful functions. Like `put()`
|
||||
--- and `put_text()` which print Lua objects to command line and current
|
||||
--- buffer respectively.
|
||||
---
|
||||
--- - |MiniMove| - move any selection in any direction. Supports any Visual
|
||||
--- mode (charwise, linewise, blockwise) and Normal mode (current line) for
|
||||
--- all four directions (left, right, down, up). Respects `count` and undo.
|
||||
---
|
||||
--- - |MiniNotify| - show one or more highlighted notifications in a single window.
|
||||
--- Provides both low-level functions (add, update, remove, clear) and maker
|
||||
--- of |vim.notify()| implementation. Sets up automated LSP progress updates.
|
||||
---
|
||||
--- - |MiniOperators| - various text edit operators: replace, exchange,
|
||||
--- multiply, sort, evaluate. Creates mappings to operate on textobject,
|
||||
--- line, and visual selection. Supports |[count]| and dot-repeat.
|
||||
---
|
||||
--- - |MiniPairs| - autopairs plugin which has minimal defaults and
|
||||
--- functionality to do per-key expression mappings.
|
||||
---
|
||||
--- - |MiniPick| - general purpose interactive non-blocking picker with
|
||||
--- toggleable preview. Has fast default matching with fuzzy/exact/grouped
|
||||
--- modes. Provides most used built-in pickers for files, pattern matches,
|
||||
--- buffers, etc. For full experience needs enabled |MiniIcons| module (but
|
||||
--- works without it).
|
||||
---
|
||||
--- - |MiniSessions| - session management (read, write, delete) which works
|
||||
--- using |mksession|. Implements both global (from configured directory) and
|
||||
--- local (from current directory) sessions.
|
||||
---
|
||||
--- - |MiniSplitjoin| - split and join arguments (regions inside brackets
|
||||
--- between allowed separators). Has customizable pre and post hooks.
|
||||
--- Works inside comments.
|
||||
---
|
||||
--- - |MiniStarter| - minimal, fast, and flexible start screen. Displayed items
|
||||
--- are fully customizable both in terms of what they do and how they look
|
||||
--- (with reasonable defaults). Item selection can be done using prefix query
|
||||
--- with instant visual feedback.
|
||||
---
|
||||
--- - |MiniStatusline| - minimal and fast statusline. Has ability to use custom
|
||||
--- content supplied with concise function (using module's provided section
|
||||
--- functions) along with builtin default. For full experience needs
|
||||
--- enabled |MiniDiff|, |MiniGit|, and |MiniIcons| modules (but works without
|
||||
--- any of them).
|
||||
---
|
||||
--- - |MiniSurround| - fast and feature-rich surround plugin. Add, delete,
|
||||
--- replace, find, highlight surrounding (like pair of parenthesis, quotes,
|
||||
--- etc.). Supports dot-repeat, `v:count`, different search methods,
|
||||
--- "last"/"next" extended mappings, customization via Lua patterns or
|
||||
--- functions, and more. Has builtins for brackets, function call, tag, user
|
||||
--- prompt, and any alphanumeric/punctuation/whitespace character.
|
||||
---
|
||||
--- - |MiniTest| - framework for writing extensive Neovim plugin tests.
|
||||
--- Supports hierarchical tests, hooks, parametrization, filtering (like from
|
||||
--- current file or cursor position), screen tests, "busted-style" emulation,
|
||||
--- customizable reporters, and more. Designed to be used with provided
|
||||
--- wrapper for managing child Neovim processes.
|
||||
---
|
||||
--- - |MiniTabline| - minimal tabline which always shows listed (see 'buflisted')
|
||||
--- buffers. Allows showing extra information section in case of multiple vim
|
||||
--- tabpages. For full experience needs enabled |MiniIcons| module (but works
|
||||
--- without it).
|
||||
---
|
||||
--- - |MiniTrailspace| - automatic highlighting of trailing whitespace with
|
||||
--- functionality to remove it.
|
||||
---
|
||||
--- - |MiniVisits| - track and reuse file system visits. Tracks data about each
|
||||
--- file/directory visit (after delay) and stores it (only) locally. This can be
|
||||
--- used to get a list of "recent"/"frequent"/"frecent" visits.
|
||||
--- Allows persistently adding labels to visits enabling flexible workflow.
|
||||
|
||||
--- Common recipes for disabling functionality
|
||||
---
|
||||
--- Each module's core functionality can be disabled globally or buffer-locally
|
||||
--- by creating appropriate global or buffer-scoped variables equal to |v:true|.
|
||||
--- Functionality is disabled if at least one of |g:| or |b:| variables is `v:true`.
|
||||
---
|
||||
--- Variable names have the same structure: `{g,b}:mini*_disable` where `*` is
|
||||
--- module's lowercase name. For example, `g:minianimate_disable` disables
|
||||
--- |mini.animate| globally and `b:minianimate_disable` - for current buffer.
|
||||
--- Note: in this section disabling 'mini.animate' is used as example;
|
||||
--- everything holds for other module variables.
|
||||
---
|
||||
--- Considering high number of different scenarios and customization intentions,
|
||||
--- writing exact rules for disabling module's functionality is left to user.
|
||||
---
|
||||
--- # Manual disabling ~
|
||||
--- >lua
|
||||
--- -- Disable globally
|
||||
--- vim.g.minianimate_disable = true
|
||||
---
|
||||
--- -- Disable for current buffer
|
||||
--- vim.b.minianimate_disable = true
|
||||
---
|
||||
--- -- Toggle (disable if enabled, enable if disabled)
|
||||
--- vim.g.minianimate_disable = not vim.g.minianimate_disable -- globally
|
||||
--- vim.b.minianimate_disable = not vim.b.minianimate_disable -- for buffer
|
||||
--- <
|
||||
--- # Automated disabling ~
|
||||
---
|
||||
--- Automated disabling is suggested to be done inside autocommands: >lua
|
||||
---
|
||||
--- -- Disable for a certain filetype (for example, "lua")
|
||||
--- local f = function(args) vim.b[args.buf].minianimate_disable = true end
|
||||
--- vim.api.nvim_create_autocmd('Filetype', { pattern = 'lua', callback = f })
|
||||
---
|
||||
--- -- Enable only for certain filetypes (for example, "lua" and "help")
|
||||
--- local f = function(args)
|
||||
--- local ft = vim.bo[args.buf].filetype
|
||||
--- if ft == 'lua' or ft == 'help' then return end
|
||||
--- vim.b[args.buf].minianimate_disable = true
|
||||
--- end
|
||||
--- vim.api.nvim_create_autocmd('Filetype', { callback = f })
|
||||
---
|
||||
--- -- Disable in Visual mode
|
||||
--- local f_en = function(args) vim.b[args.buf].minianimate_disable = false end
|
||||
--- local enable_opts = { pattern = '[vV\x16]*:*', callback = f_en }
|
||||
--- vim.api.nvim_create_autocmd('ModeChanged', enable_opts)
|
||||
---
|
||||
--- local f_dis = function(args) vim.b[args.buf].minianimate_disable = true end
|
||||
--- local disable_opts = { pattern = '*:[vV\x16]*', callback = f_dis }
|
||||
--- vim.api.nvim_create_autocmd('ModeChanged', disable_opts)
|
||||
---
|
||||
--- -- Disable in Terminal buffer
|
||||
--- local f = function(args) vim.b[args.buf].minianimate_disable = true end
|
||||
--- vim.api.nvim_create_autocmd('TermOpen', { callback = f })
|
||||
--- <
|
||||
---@tag mini.nvim-disabling-recipes
|
||||
|
||||
--- Buffer local config
|
||||
---
|
||||
--- Each module can be additionally configured locally to buffer by creating
|
||||
--- appropriate buffer-scoped variable with values you want to override. It
|
||||
--- will affect only runtime options and not those used once during setup (like
|
||||
--- `mappings` or `set_vim_settings`).
|
||||
---
|
||||
--- Variable names have the same structure: `b:mini*_config` where `*` is
|
||||
--- module's lowercase name. For example, `b:minianimate_config` can store
|
||||
--- information about how |mini.animate| will act inside current buffer. Its
|
||||
--- value should be a table with same structure as module's `config`. Example: >lua
|
||||
---
|
||||
--- -- Disable scroll animation in current buffer
|
||||
--- vim.b.minianimate_config = { scroll = { enable = false } }
|
||||
--- <
|
||||
--- Considering high number of different scenarios and customization intentions,
|
||||
--- writing exact rules for module's buffer local configuration is left to
|
||||
--- user. It is done in similar fashion to |mini.nvim-disabling-recipes|.
|
||||
---@tag mini.nvim-buffer-local-config
|
||||
|
||||
vim.notify([[Do not `require('mini')` directly. Setup every module separately.]])
|
||||
|
||||
return {}
|
||||
541
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/jump.lua
Normal file
541
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/jump.lua
Normal file
@ -0,0 +1,541 @@
|
||||
--- *mini.jump* Jump to next/previous single character
|
||||
--- *MiniJump*
|
||||
---
|
||||
--- MIT License Copyright (c) 2021 Evgeni Chasnovski, Adam Blažek
|
||||
---
|
||||
--- ==============================================================================
|
||||
---
|
||||
--- Features:
|
||||
--- - Extend f, F, t, T to work on multiple lines.
|
||||
---
|
||||
--- - Repeat jump by pressing f, F, t, T again. It is reset when cursor moved
|
||||
--- as a result of not jumping or timeout after idle time (duration
|
||||
--- customizable).
|
||||
---
|
||||
--- - Highlight (after customizable delay) all possible target characters and
|
||||
--- stop it after some (customizable) idle time.
|
||||
---
|
||||
--- - Normal, Visual, and Operator-pending (with full dot-repeat) modes are
|
||||
--- supported.
|
||||
---
|
||||
--- This module follows vim's 'ignorecase' and 'smartcase' options. When
|
||||
--- 'ignorecase' is set, f, F, t, T will match case-insensitively. When
|
||||
--- 'smartcase' is also set, f, F, t, T will only match lowercase
|
||||
--- characters case-insensitively.
|
||||
---
|
||||
--- # Setup ~
|
||||
---
|
||||
--- This module needs a setup with `require('mini.jump').setup({})`
|
||||
--- (replace `{}` with your `config` table). It will create global Lua table
|
||||
--- `MiniJump` which you can use for scripting or manually (with
|
||||
--- `:lua MiniJump.*`).
|
||||
---
|
||||
--- See |MiniJump.config| for `config` structure and default values.
|
||||
---
|
||||
--- You can override runtime config settings locally to buffer inside
|
||||
--- `vim.b.minijump_config` which should have same structure as
|
||||
--- `MiniJump.config`. See |mini.nvim-buffer-local-config| for more details.
|
||||
---
|
||||
--- To stop module from showing non-error feedback, set `config.silent = true`.
|
||||
---
|
||||
--- # Highlight groups ~
|
||||
---
|
||||
--- * `MiniJump` - all possible cursor positions.
|
||||
---
|
||||
--- To change any highlight group, modify it directly with |:highlight|.
|
||||
---
|
||||
--- # Disabling ~
|
||||
---
|
||||
--- To disable core functionality, set `vim.g.minijump_disable` (globally) or
|
||||
--- `vim.b.minijump_disable` (for a buffer) to `true`. Considering high number of
|
||||
--- different scenarios and customization intentions, writing exact rules for
|
||||
--- disabling module's functionality is left to user. See
|
||||
--- |mini.nvim-disabling-recipes| for common recipes.
|
||||
|
||||
---@alias __jump_target string|nil The string to jump to.
|
||||
---@alias __jump_backward boolean|nil Whether to jump backward.
|
||||
---@alias __jump_till boolean|nil Whether to jump just before/after the match instead of
|
||||
--- exactly on target. This includes positioning cursor past the end of
|
||||
--- previous/current line. Note that with backward jump this might lead to
|
||||
--- cursor being on target if can't be put past the line.
|
||||
---@alias __jump_n_times number|nil Number of times to perform consecutive jumps.
|
||||
|
||||
-- Module definition ==========================================================
|
||||
local MiniJump = {}
|
||||
local H = {}
|
||||
|
||||
--- Module setup
|
||||
---
|
||||
---@param config table|nil Module config table. See |MiniJump.config|.
|
||||
---
|
||||
---@usage >lua
|
||||
--- require('mini.jump').setup() -- use default config
|
||||
--- -- OR
|
||||
--- require('mini.jump').setup({}) -- replace {} with your config table
|
||||
--- <
|
||||
MiniJump.setup = function(config)
|
||||
-- Export module
|
||||
_G.MiniJump = MiniJump
|
||||
|
||||
-- Setup config
|
||||
config = H.setup_config(config)
|
||||
|
||||
-- Apply config
|
||||
H.apply_config(config)
|
||||
|
||||
-- Define behavior
|
||||
H.create_autocommands()
|
||||
|
||||
-- Create default highlighting
|
||||
H.create_default_hl()
|
||||
end
|
||||
|
||||
--- Module config
|
||||
---
|
||||
--- Default values:
|
||||
---@eval return MiniDoc.afterlines_to_code(MiniDoc.current.eval_section)
|
||||
MiniJump.config = {
|
||||
-- Module mappings. Use `''` (empty string) to disable one.
|
||||
mappings = {
|
||||
forward = 'f',
|
||||
backward = 'F',
|
||||
forward_till = 't',
|
||||
backward_till = 'T',
|
||||
repeat_jump = ';',
|
||||
},
|
||||
|
||||
-- Delay values (in ms) for different functionalities. Set any of them to
|
||||
-- a very big number (like 10^7) to virtually disable.
|
||||
delay = {
|
||||
-- Delay between jump and highlighting all possible jumps
|
||||
highlight = 250,
|
||||
|
||||
-- Delay between jump and automatic stop if idle (no jump is done)
|
||||
idle_stop = 10000000,
|
||||
},
|
||||
|
||||
-- Whether to disable showing non-error feedback
|
||||
silent = false,
|
||||
}
|
||||
--minidoc_afterlines_end
|
||||
|
||||
-- Module data ================================================================
|
||||
--- Data about jumping state
|
||||
---
|
||||
--- It stores various information used in this module. All elements, except
|
||||
--- `jumping`, is about the latest jump. They are used as default values for
|
||||
--- similar arguments.
|
||||
---
|
||||
---@class JumpingState
|
||||
---
|
||||
---@field target __jump_target
|
||||
---@field backward __jump_backward
|
||||
---@field till __jump_till
|
||||
---@field n_times __jump_n_times
|
||||
---@field mode string Mode of latest jump (output of |mode()| with non-zero argument).
|
||||
---@field jumping boolean Whether module is currently in "jumping mode": usage of
|
||||
--- |MiniJump.smart_jump| and all mappings won't require target.
|
||||
---@text
|
||||
--- Initial values:
|
||||
---@eval return MiniDoc.afterlines_to_code(MiniDoc.current.eval_section)
|
||||
MiniJump.state = {
|
||||
target = nil,
|
||||
backward = false,
|
||||
till = false,
|
||||
n_times = 1,
|
||||
mode = nil,
|
||||
jumping = false,
|
||||
}
|
||||
--minidoc_afterlines_end
|
||||
|
||||
-- Module functionality =======================================================
|
||||
--- Jump to target
|
||||
---
|
||||
--- Takes a string and jumps to its first occurrence in desired direction.
|
||||
---
|
||||
--- All default values are taken from |MiniJump.state| to emulate latest jump.
|
||||
---
|
||||
---@param target __jump_target
|
||||
---@param backward __jump_backward
|
||||
---@param till __jump_till
|
||||
---@param n_times __jump_n_times
|
||||
MiniJump.jump = function(target, backward, till, n_times)
|
||||
if H.is_disabled() then return end
|
||||
|
||||
-- Cache inputs for future use
|
||||
H.update_state(target, backward, till, n_times)
|
||||
|
||||
if MiniJump.state.target == nil then
|
||||
H.message('Can not jump because there is no recent `target`.')
|
||||
return
|
||||
end
|
||||
|
||||
-- Determine if target is present anywhere in order to correctly enter
|
||||
-- jumping mode. If not, jumping mode is not possible.
|
||||
local search_pattern = [[\V]] .. vim.fn.escape(MiniJump.state.target, [[\]])
|
||||
local target_is_present = vim.fn.search(search_pattern, 'wn') ~= 0
|
||||
if not target_is_present then return end
|
||||
|
||||
-- Construct search and highlight pattern data
|
||||
local pattern, hl_pattern, flags = H.make_search_data()
|
||||
|
||||
-- Delay highlighting after stopping previous one
|
||||
local config = H.get_config()
|
||||
H.timers.highlight:stop()
|
||||
H.timers.highlight:start(
|
||||
-- Update highlighting immediately if any highlighting is already present
|
||||
H.is_highlighting() and 0 or config.delay.highlight,
|
||||
0,
|
||||
vim.schedule_wrap(function() H.highlight(hl_pattern) end)
|
||||
)
|
||||
|
||||
-- Start idle timer after stopping previous one
|
||||
H.timers.idle_stop:stop()
|
||||
H.timers.idle_stop:start(config.delay.idle_stop, 0, vim.schedule_wrap(function() MiniJump.stop_jumping() end))
|
||||
|
||||
-- Make jump(s)
|
||||
H.cache.n_cursor_moved = 0
|
||||
local init_cursor_data = H.get_cursor_data()
|
||||
MiniJump.state.jumping = true
|
||||
for _ = 1, MiniJump.state.n_times do
|
||||
vim.fn.search(pattern, flags)
|
||||
end
|
||||
|
||||
-- Open enough folds to show jump
|
||||
vim.cmd('normal! zv')
|
||||
|
||||
-- Track cursor position to account for movement not caught by `CursorMoved`
|
||||
H.cache.latest_cursor = H.get_cursor_data()
|
||||
H.cache.has_changed_cursor = not vim.deep_equal(H.cache.latest_cursor, init_cursor_data)
|
||||
end
|
||||
|
||||
--- Make smart jump
|
||||
---
|
||||
--- If the last movement was a jump, perform another jump with the same target.
|
||||
--- Otherwise, wait for a target input (via |getcharstr()|). Respects |v:count|.
|
||||
---
|
||||
--- All default values are taken from |MiniJump.state| to emulate latest jump.
|
||||
---
|
||||
---@param backward __jump_backward
|
||||
---@param till __jump_till
|
||||
MiniJump.smart_jump = function(backward, till)
|
||||
if H.is_disabled() then return end
|
||||
|
||||
-- Jumping should stop after mode change (use `mode(1)` to track 'omap' case)
|
||||
-- or if cursor has moved after latest jump
|
||||
local has_changed_mode = MiniJump.state.mode ~= vim.fn.mode(1)
|
||||
local has_changed_cursor = not vim.deep_equal(H.cache.latest_cursor, H.get_cursor_data())
|
||||
if has_changed_mode or has_changed_cursor then MiniJump.stop_jumping() end
|
||||
|
||||
-- Ask for target only when needed
|
||||
local target
|
||||
if not MiniJump.state.jumping or MiniJump.state.target == nil then
|
||||
target = H.get_target()
|
||||
-- Stop if user supplied invalid target
|
||||
if target == nil then return end
|
||||
end
|
||||
|
||||
H.update_state(target, backward, till, vim.v.count1)
|
||||
|
||||
MiniJump.jump()
|
||||
end
|
||||
|
||||
--- Stop jumping
|
||||
---
|
||||
--- Removes highlights (if any) and forces the next smart jump to prompt for
|
||||
--- the target. Automatically called on appropriate Neovim |events|.
|
||||
MiniJump.stop_jumping = function()
|
||||
H.timers.highlight:stop()
|
||||
H.timers.idle_stop:stop()
|
||||
|
||||
MiniJump.state.jumping = false
|
||||
|
||||
H.cache.n_cursor_moved = 0
|
||||
H.cache.latest_cursor = nil
|
||||
H.cache.msg_shown = false
|
||||
|
||||
H.unhighlight()
|
||||
end
|
||||
|
||||
-- Helper data ================================================================
|
||||
-- Module default config
|
||||
H.default_config = vim.deepcopy(MiniJump.config)
|
||||
|
||||
-- Cache for various operations
|
||||
H.cache = {
|
||||
-- Counter of number of CursorMoved events
|
||||
n_cursor_moved = 0,
|
||||
|
||||
-- Latest cursor position data
|
||||
latest_cursor = nil,
|
||||
|
||||
-- Whether helper message was shown
|
||||
msg_shown = false,
|
||||
}
|
||||
|
||||
-- Timers for different delay-related functionalities
|
||||
H.timers = { highlight = vim.loop.new_timer(), idle_stop = vim.loop.new_timer() }
|
||||
|
||||
-- Information about last match highlighting (stored *per window*):
|
||||
-- - Key: windows' unique buffer identifiers.
|
||||
-- - Value: table with:
|
||||
-- - `id` field for match id (from `vim.fn.matchadd()`).
|
||||
-- - `pattern` field for highlighted pattern.
|
||||
H.window_matches = {}
|
||||
|
||||
-- Helper functionality =======================================================
|
||||
-- Settings -------------------------------------------------------------------
|
||||
H.setup_config = function(config)
|
||||
-- General idea: if some table elements are not present in user-supplied
|
||||
-- `config`, take them from default config
|
||||
vim.validate({ config = { config, 'table', true } })
|
||||
config = vim.tbl_deep_extend('force', vim.deepcopy(H.default_config), config or {})
|
||||
|
||||
-- Validate per nesting level to produce correct error message
|
||||
vim.validate({
|
||||
mappings = { config.mappings, 'table' },
|
||||
delay = { config.delay, 'table' },
|
||||
silent = { config.silent, 'boolean' },
|
||||
})
|
||||
|
||||
vim.validate({
|
||||
['delay.highlight'] = { config.delay.highlight, 'number' },
|
||||
['delay.idle_stop'] = { config.delay.idle_stop, 'number' },
|
||||
|
||||
['mappings.forward'] = { config.mappings.forward, 'string' },
|
||||
['mappings.backward'] = { config.mappings.backward, 'string' },
|
||||
['mappings.forward_till'] = { config.mappings.forward_till, 'string' },
|
||||
['mappings.backward_till'] = { config.mappings.backward_till, 'string' },
|
||||
['mappings.repeat_jump'] = { config.mappings.repeat_jump, 'string' },
|
||||
})
|
||||
|
||||
return config
|
||||
end
|
||||
|
||||
H.apply_config = function(config)
|
||||
MiniJump.config = config
|
||||
|
||||
--stylua: ignore start
|
||||
H.map('n', config.mappings.forward, '<Cmd>lua MiniJump.smart_jump(false, false)<CR>', { desc = 'Jump forward' })
|
||||
H.map('n', config.mappings.backward, '<Cmd>lua MiniJump.smart_jump(true, false)<CR>', { desc = 'Jump backward' })
|
||||
H.map('n', config.mappings.forward_till, '<Cmd>lua MiniJump.smart_jump(false, true)<CR>', { desc = 'Jump forward till' })
|
||||
H.map('n', config.mappings.backward_till, '<Cmd>lua MiniJump.smart_jump(true, true)<CR>', { desc = 'Jump backward till' })
|
||||
H.map('n', config.mappings.repeat_jump, '<Cmd>lua MiniJump.jump()<CR>', { desc = 'Repeat jump' })
|
||||
|
||||
H.map('x', config.mappings.forward, '<Cmd>lua MiniJump.smart_jump(false, false)<CR>', { desc = 'Jump forward' })
|
||||
H.map('x', config.mappings.backward, '<Cmd>lua MiniJump.smart_jump(true, false)<CR>', { desc = 'Jump backward' })
|
||||
H.map('x', config.mappings.forward_till, '<Cmd>lua MiniJump.smart_jump(false, true)<CR>', { desc = 'Jump forward till' })
|
||||
H.map('x', config.mappings.backward_till, '<Cmd>lua MiniJump.smart_jump(true, true)<CR>', { desc = 'Jump backward till' })
|
||||
H.map('x', config.mappings.repeat_jump, '<Cmd>lua MiniJump.jump()<CR>', { desc = 'Repeat jump' })
|
||||
|
||||
H.map('o', config.mappings.forward, H.make_expr_jump(false, false), { expr = true, desc = 'Jump forward' })
|
||||
H.map('o', config.mappings.backward, H.make_expr_jump(true, false), { expr = true, desc = 'Jump backward' })
|
||||
H.map('o', config.mappings.forward_till, H.make_expr_jump(false, true), { expr = true, desc = 'Jump forward till' })
|
||||
H.map('o', config.mappings.backward_till, H.make_expr_jump(true, true), { expr = true, desc = 'Jump backward till' })
|
||||
H.map('o', config.mappings.repeat_jump, H.make_expr_jump(), { expr = true, desc = 'Repeat jump' })
|
||||
--stylua: ignore end
|
||||
end
|
||||
|
||||
H.create_autocommands = function()
|
||||
local augroup = vim.api.nvim_create_augroup('MiniJump', {})
|
||||
|
||||
local au = function(event, pattern, callback, desc)
|
||||
vim.api.nvim_create_autocmd(event, { group = augroup, pattern = pattern, callback = callback, desc = desc })
|
||||
end
|
||||
|
||||
au('CursorMoved', '*', H.on_cursormoved, 'On CursorMoved')
|
||||
au({ 'BufLeave', 'InsertEnter' }, '*', MiniJump.stop_jumping, 'Stop jumping')
|
||||
end
|
||||
|
||||
H.create_default_hl = function() vim.api.nvim_set_hl(0, 'MiniJump', { default = true, link = 'SpellRare' }) end
|
||||
|
||||
H.is_disabled = function() return vim.g.minijump_disable == true or vim.b.minijump_disable == true end
|
||||
|
||||
H.get_config = function(config)
|
||||
return vim.tbl_deep_extend('force', MiniJump.config, vim.b.minijump_config or {}, config or {})
|
||||
end
|
||||
|
||||
-- Mappings -------------------------------------------------------------------
|
||||
H.make_expr_jump = function(backward, till)
|
||||
return function()
|
||||
if H.is_disabled() then return '' end
|
||||
|
||||
-- Ask for `target` for non-repeating jump as this will be used only in
|
||||
-- operator-pending mode. Dot-repeat is supported via expression-mapping.
|
||||
local is_repeat_jump = backward == nil or till == nil
|
||||
local target = is_repeat_jump and MiniJump.state.target or H.get_target()
|
||||
|
||||
-- Stop if user supplied invalid target
|
||||
if target == nil then return '<Esc>' end
|
||||
H.update_state(target, backward, till, vim.v.count1)
|
||||
|
||||
vim.schedule(function()
|
||||
if H.cache.has_changed_cursor then return end
|
||||
vim.cmd('undo!')
|
||||
end)
|
||||
return 'v<Cmd>lua MiniJump.jump()<CR>'
|
||||
end
|
||||
end
|
||||
|
||||
-- Autocommands ---------------------------------------------------------------
|
||||
H.on_cursormoved = function()
|
||||
-- Check if jumping to avoid unnecessary actions on every CursorMoved
|
||||
if MiniJump.state.jumping then
|
||||
H.cache.n_cursor_moved = H.cache.n_cursor_moved + 1
|
||||
-- Stop jumping only if `CursorMoved` was not a result of smart jump
|
||||
if H.cache.n_cursor_moved > 1 then MiniJump.stop_jumping() end
|
||||
end
|
||||
end
|
||||
|
||||
-- Pattern matching -----------------------------------------------------------
|
||||
H.make_search_data = function()
|
||||
local target = vim.fn.escape(MiniJump.state.target, [[\]])
|
||||
local backward, till = MiniJump.state.backward, MiniJump.state.till
|
||||
|
||||
local flags = backward and 'Wb' or 'W'
|
||||
local pattern, hl_pattern
|
||||
|
||||
if till then
|
||||
-- General logic: moving pattern should match just before/after target,
|
||||
-- while highlight pattern should match target for every "movable" place.
|
||||
-- Also allow checking for "just before/after" across lines by accepting
|
||||
-- `\n` as possible match.
|
||||
if backward then
|
||||
-- NOTE: use `\@<=` instead of `\zs` because it behaves better in case of
|
||||
-- consecutive matches (like `xxxx` for target `x`)
|
||||
pattern = target .. [[\@<=\_.]]
|
||||
hl_pattern = target .. [[\ze\_.]]
|
||||
else
|
||||
pattern = [[\_.\ze]] .. target
|
||||
hl_pattern = [[\_.\@<=]] .. target
|
||||
end
|
||||
else
|
||||
local is_visual = vim.tbl_contains({ 'v', 'V', '\22' }, vim.fn.mode())
|
||||
local is_exclusive = vim.o.selection == 'exclusive'
|
||||
if not backward and is_visual and is_exclusive then
|
||||
-- Still select target in case of exclusive visual selection
|
||||
pattern = target .. [[\zs\_.]]
|
||||
hl_pattern = target .. [[\ze\_.]]
|
||||
else
|
||||
pattern = target
|
||||
hl_pattern = target
|
||||
end
|
||||
end
|
||||
|
||||
-- Enable 'very nomagic' mode and possibly case-insensitivity
|
||||
local ignore_case = vim.o.ignorecase and (not vim.o.smartcase or target == target:lower())
|
||||
local prefix = ignore_case and [[\V\c]] or [[\V]]
|
||||
pattern, hl_pattern = prefix .. pattern, prefix .. hl_pattern
|
||||
|
||||
return pattern, hl_pattern, flags
|
||||
end
|
||||
|
||||
-- Highlighting ---------------------------------------------------------------
|
||||
H.highlight = function(pattern)
|
||||
-- Don't do anything if already highlighting input pattern
|
||||
if H.is_highlighting(pattern) then return end
|
||||
|
||||
-- Stop highlighting possible previous pattern. Needed to adjust highlighting
|
||||
-- when inside jumping but a different kind one. Example: first jump with
|
||||
-- `till = false` and then, without jumping stop, jump to same character with
|
||||
-- `till = true`. If this character is first on line, highlighting should change
|
||||
H.unhighlight()
|
||||
|
||||
-- Never highlight in Insert mode
|
||||
if vim.fn.mode() == 'i' then return end
|
||||
|
||||
local match_id = vim.fn.matchadd('MiniJump', pattern)
|
||||
H.window_matches[vim.api.nvim_get_current_win()] = { id = match_id, pattern = pattern }
|
||||
end
|
||||
|
||||
H.unhighlight = function()
|
||||
-- Remove highlighting from all windows as jumping is intended to work only
|
||||
-- in current window. This will work also from other (usually popup) window.
|
||||
for win_id, match_info in pairs(H.window_matches) do
|
||||
if vim.api.nvim_win_is_valid(win_id) then
|
||||
-- Use `pcall` because there is an error if match id is not present. It
|
||||
-- can happen if something else called `clearmatches`.
|
||||
pcall(vim.fn.matchdelete, match_info.id, win_id)
|
||||
H.window_matches[win_id] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---@param pattern string|nil Highlight pattern to check for. If `nil`, checks for
|
||||
--- any highlighting registered in current window.
|
||||
---@private
|
||||
H.is_highlighting = function(pattern)
|
||||
local win_id = vim.api.nvim_get_current_win()
|
||||
local match_info = H.window_matches[win_id]
|
||||
if match_info == nil then return false end
|
||||
return pattern == nil or match_info.pattern == pattern
|
||||
end
|
||||
|
||||
-- Utilities ------------------------------------------------------------------
|
||||
H.echo = function(msg, is_important)
|
||||
if H.get_config().silent then return end
|
||||
|
||||
-- Construct message chunks
|
||||
msg = type(msg) == 'string' and { { msg } } or msg
|
||||
table.insert(msg, 1, { '(mini.jump) ', 'WarningMsg' })
|
||||
|
||||
-- Avoid hit-enter-prompt
|
||||
local chunks = msg
|
||||
if not is_important then
|
||||
chunks = {}
|
||||
local max_width = vim.o.columns * math.max(vim.o.cmdheight - 1, 0) + vim.v.echospace
|
||||
local tot_width = 0
|
||||
for _, ch in ipairs(msg) do
|
||||
local new_ch = { vim.fn.strcharpart(ch[1], 0, max_width - tot_width), ch[2] }
|
||||
table.insert(chunks, new_ch)
|
||||
tot_width = tot_width + vim.fn.strdisplaywidth(new_ch[1])
|
||||
if tot_width >= max_width then break end
|
||||
end
|
||||
end
|
||||
|
||||
-- Echo. Force redraw to ensure that it is effective (`:h echo-redraw`)
|
||||
vim.cmd([[echo '' | redraw]])
|
||||
vim.api.nvim_echo(chunks, is_important, {})
|
||||
end
|
||||
|
||||
H.unecho = function()
|
||||
if H.cache.msg_shown then vim.cmd([[echo '' | redraw]]) end
|
||||
end
|
||||
|
||||
H.message = function(msg) H.echo(msg, true) end
|
||||
|
||||
H.update_state = function(target, backward, till, n_times)
|
||||
MiniJump.state.mode = vim.fn.mode(1)
|
||||
|
||||
-- Don't use `? and <1> or <2>` because it doesn't work when `<1>` is `false`
|
||||
if target ~= nil then MiniJump.state.target = target end
|
||||
if backward ~= nil then MiniJump.state.backward = backward end
|
||||
if till ~= nil then MiniJump.state.till = till end
|
||||
if n_times ~= nil then MiniJump.state.n_times = n_times end
|
||||
end
|
||||
|
||||
H.get_cursor_data = function() return { vim.api.nvim_get_current_win(), vim.api.nvim_win_get_cursor(0) } end
|
||||
|
||||
H.get_target = function()
|
||||
local needs_help_msg = true
|
||||
vim.defer_fn(function()
|
||||
if not needs_help_msg then return end
|
||||
H.echo('Enter target single character ')
|
||||
H.cache.msg_shown = true
|
||||
end, 1000)
|
||||
local ok, char = pcall(vim.fn.getcharstr)
|
||||
needs_help_msg = false
|
||||
H.unecho()
|
||||
|
||||
-- Terminate if couldn't get input (like with <C-c>) or it is `<Esc>`
|
||||
if not ok or char == '\27' then return end
|
||||
return char
|
||||
end
|
||||
|
||||
H.map = function(mode, lhs, rhs, opts)
|
||||
if lhs == '' then return end
|
||||
opts = vim.tbl_deep_extend('force', { silent = true }, opts or {})
|
||||
vim.keymap.set(mode, lhs, rhs, opts)
|
||||
end
|
||||
|
||||
return MiniJump
|
||||
1169
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/jump2d.lua
Normal file
1169
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/jump2d.lua
Normal file
File diff suppressed because it is too large
Load Diff
1691
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/map.lua
Normal file
1691
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/map.lua
Normal file
File diff suppressed because it is too large
Load Diff
645
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/misc.lua
Normal file
645
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/misc.lua
Normal file
@ -0,0 +1,645 @@
|
||||
--- *mini.misc* Miscellaneous functions
|
||||
--- *MiniMisc*
|
||||
---
|
||||
--- MIT License Copyright (c) 2021 Evgeni Chasnovski
|
||||
---
|
||||
--- ==============================================================================
|
||||
---
|
||||
--- Features the following functions:
|
||||
--- - |MiniMisc.bench_time()| to benchmark function execution time.
|
||||
--- Useful in combination with `stat_summary()`.
|
||||
---
|
||||
--- - |MiniMisc.put()| and |MiniMisc.put_text()| to pretty print its arguments
|
||||
--- into command line and current buffer respectively.
|
||||
---
|
||||
--- - |MiniMisc.setup_auto_root()| to set up automated change of current directory.
|
||||
---
|
||||
--- - |MiniMisc.setup_termbg_sync()| to set up terminal background synchronization
|
||||
--- (removes possible "frame" around current Neovim instance).
|
||||
---
|
||||
--- - |MiniMisc.setup_restore_cursor()| to set up automated restoration of
|
||||
--- cursor position on file reopen.
|
||||
---
|
||||
--- - |MiniMisc.stat_summary()| to compute summary statistics of numerical array.
|
||||
--- Useful in combination with `bench_time()`.
|
||||
---
|
||||
--- - |MiniMisc.tbl_head()| and |MiniMisc.tbl_tail()| to return "first" and "last"
|
||||
--- elements of table.
|
||||
---
|
||||
--- - |MiniMisc.zoom()| to zoom in and out of a buffer, making it full screen
|
||||
--- in a floating window.
|
||||
---
|
||||
--- - And more.
|
||||
---
|
||||
--- # Setup ~
|
||||
---
|
||||
--- This module doesn't need setup, but it can be done to improve usability.
|
||||
--- Setup with `require('mini.misc').setup({})` (replace `{}` with your
|
||||
--- `config` table). It will create global Lua table `MiniMisc` which you can
|
||||
--- use for scripting or manually (with `:lua MiniMisc.*`).
|
||||
---
|
||||
--- See |MiniMisc.config| for `config` structure and default values.
|
||||
---
|
||||
--- This module doesn't have runtime options, so using `vim.b.minimisc_config`
|
||||
--- will have no effect here.
|
||||
|
||||
-- Module definition ==========================================================
|
||||
local MiniMisc = {}
|
||||
local H = {}
|
||||
|
||||
--- Module setup
|
||||
---
|
||||
---@param config table|nil Module config table. See |MiniMisc.config|.
|
||||
---
|
||||
---@usage >lua
|
||||
--- require('mini.misc').setup() -- use default config
|
||||
--- -- OR
|
||||
--- require('mini.misc').setup({}) -- replace {} with your config table
|
||||
--- <
|
||||
MiniMisc.setup = function(config)
|
||||
-- Export module
|
||||
_G.MiniMisc = MiniMisc
|
||||
|
||||
-- Setup config
|
||||
config = H.setup_config(config)
|
||||
|
||||
-- Apply config
|
||||
H.apply_config(config)
|
||||
end
|
||||
|
||||
--- Module config
|
||||
---
|
||||
--- Default values:
|
||||
---@eval return MiniDoc.afterlines_to_code(MiniDoc.current.eval_section)
|
||||
MiniMisc.config = {
|
||||
-- Array of fields to make global (to be used as independent variables)
|
||||
make_global = { 'put', 'put_text' },
|
||||
}
|
||||
--minidoc_afterlines_end
|
||||
|
||||
-- Module functionality =======================================================
|
||||
--- Execute `f` several times and time how long it took
|
||||
---
|
||||
---@param f function Function which execution to benchmark.
|
||||
---@param n number|nil Number of times to execute `f(...)`. Default: 1.
|
||||
---@param ... any Arguments when calling `f`.
|
||||
---
|
||||
---@return ... Table with durations (in seconds; up to nanoseconds) and
|
||||
--- output of (last) function execution.
|
||||
MiniMisc.bench_time = function(f, n, ...)
|
||||
n = n or 1
|
||||
local durations, output = {}, nil
|
||||
for _ = 1, n do
|
||||
local start_time = vim.loop.hrtime()
|
||||
output = f(...)
|
||||
local end_time = vim.loop.hrtime()
|
||||
table.insert(durations, 0.000000001 * (end_time - start_time))
|
||||
end
|
||||
|
||||
return durations, output
|
||||
end
|
||||
|
||||
--- Compute width of gutter (info column on the left of the window)
|
||||
---
|
||||
---@param win_id number|nil Window identifier (see |win_getid()|) for which gutter
|
||||
--- width is computed. Default: 0 for current.
|
||||
MiniMisc.get_gutter_width = function(win_id)
|
||||
win_id = (win_id == nil or win_id == 0) and vim.api.nvim_get_current_win() or win_id
|
||||
return vim.fn.getwininfo(win_id)[1].textoff
|
||||
end
|
||||
|
||||
--- Print Lua objects in command line
|
||||
---
|
||||
---@param ... any Any number of objects to be printed each on separate line.
|
||||
MiniMisc.put = function(...)
|
||||
local objects = {}
|
||||
-- Not using `{...}` because it removes `nil` input
|
||||
for i = 1, select('#', ...) do
|
||||
local v = select(i, ...)
|
||||
table.insert(objects, vim.inspect(v))
|
||||
end
|
||||
|
||||
print(table.concat(objects, '\n'))
|
||||
|
||||
return ...
|
||||
end
|
||||
|
||||
--- Print Lua objects in current buffer
|
||||
---
|
||||
---@param ... any Any number of objects to be printed each on separate line.
|
||||
MiniMisc.put_text = function(...)
|
||||
local objects = {}
|
||||
-- Not using `{...}` because it removes `nil` input
|
||||
for i = 1, select('#', ...) do
|
||||
local v = select(i, ...)
|
||||
table.insert(objects, vim.inspect(v))
|
||||
end
|
||||
|
||||
local lines = vim.split(table.concat(objects, '\n'), '\n')
|
||||
local lnum = vim.api.nvim_win_get_cursor(0)[1]
|
||||
vim.fn.append(lnum, lines)
|
||||
|
||||
return ...
|
||||
end
|
||||
|
||||
--- Resize window to have exact number of editable columns
|
||||
---
|
||||
---@param win_id number|nil Window identifier (see |win_getid()|) to be resized.
|
||||
--- Default: 0 for current.
|
||||
---@param text_width number|nil Number of editable columns resized window will
|
||||
--- display. Default: first element of 'colorcolumn' or otherwise 'textwidth'
|
||||
--- (using screen width as its default but not more than 79).
|
||||
MiniMisc.resize_window = function(win_id, text_width)
|
||||
win_id = win_id or 0
|
||||
text_width = text_width or H.default_text_width(win_id)
|
||||
|
||||
vim.api.nvim_win_set_width(win_id, text_width + MiniMisc.get_gutter_width(win_id))
|
||||
end
|
||||
|
||||
H.default_text_width = function(win_id)
|
||||
local buf = vim.api.nvim_win_get_buf(win_id)
|
||||
local textwidth = vim.bo[buf].textwidth
|
||||
textwidth = (textwidth == 0) and math.min(vim.o.columns, 79) or textwidth
|
||||
|
||||
local colorcolumn = vim.wo[win_id].colorcolumn
|
||||
if colorcolumn ~= '' then
|
||||
local cc = vim.split(colorcolumn, ',')[1]
|
||||
local is_cc_relative = vim.tbl_contains({ '-', '+' }, cc:sub(1, 1))
|
||||
|
||||
if is_cc_relative then
|
||||
return textwidth + tonumber(cc)
|
||||
else
|
||||
return tonumber(cc)
|
||||
end
|
||||
else
|
||||
return textwidth
|
||||
end
|
||||
end
|
||||
|
||||
--- Set up automated change of current directory
|
||||
---
|
||||
--- What it does:
|
||||
--- - Creates autocommand which on every |BufEnter| event with |MiniMisc.find_root()|
|
||||
--- finds root directory for current buffer file and sets |current-directory|
|
||||
--- to it (using |chdir()|).
|
||||
--- - Resets |autochdir| to `false`.
|
||||
---
|
||||
---@param names table|function|nil Forwarded to |MiniMisc.find_root()|.
|
||||
---@param fallback function|nil Forwarded to |MiniMisc.find_root()|.
|
||||
---
|
||||
---@usage >lua
|
||||
--- require('mini.misc').setup()
|
||||
--- MiniMisc.setup_auto_root()
|
||||
--- <
|
||||
MiniMisc.setup_auto_root = function(names, fallback)
|
||||
names = names or { '.git', 'Makefile' }
|
||||
if not (H.is_array_of(names, H.is_string) or vim.is_callable(names)) then
|
||||
H.error('Argument `names` of `setup_auto_root()` should be array of string file names or a callable.')
|
||||
end
|
||||
|
||||
fallback = fallback or function() return nil end
|
||||
if not vim.is_callable(fallback) then H.error('Argument `fallback` of `setup_auto_root()` should be callable.') end
|
||||
|
||||
-- Disable conflicting option
|
||||
vim.o.autochdir = false
|
||||
|
||||
-- Create autocommand
|
||||
local set_root = function(data)
|
||||
local root = MiniMisc.find_root(data.buf, names, fallback)
|
||||
if root == nil then return end
|
||||
vim.fn.chdir(root)
|
||||
end
|
||||
local augroup = vim.api.nvim_create_augroup('MiniMiscAutoRoot', {})
|
||||
vim.api.nvim_create_autocmd(
|
||||
'BufEnter',
|
||||
{ group = augroup, callback = set_root, desc = 'Find root and change current directory' }
|
||||
)
|
||||
end
|
||||
|
||||
--- Find root directory
|
||||
---
|
||||
--- Based on a buffer name (full path to file opened in a buffer) find a root
|
||||
--- directory. If buffer is not associated with file, returns `nil`.
|
||||
---
|
||||
--- Root directory is a directory containing at least one of pre-defined files.
|
||||
--- It is searched using |vim.fn.find()| with `upward = true` starting from
|
||||
--- directory of current buffer file until first occurrence of root file(s).
|
||||
---
|
||||
--- Notes:
|
||||
--- - Uses directory path caching to speed up computations. This means that no
|
||||
--- changes in root directory will be detected after directory path was already
|
||||
--- used in this function. Reload Neovim to account for that.
|
||||
---
|
||||
---@param buf_id number|nil Buffer identifier (see |bufnr()|) to use.
|
||||
--- Default: 0 for current.
|
||||
---@param names table|function|nil Array of file names or a callable used to
|
||||
--- identify a root directory. Forwarded to |vim.fs.find()|.
|
||||
--- Default: `{ '.git', 'Makefile' }`.
|
||||
---@param fallback function|nil Callable fallback to use if no root is found
|
||||
--- with |vim.fs.find()|. Will be called with a buffer path and should return
|
||||
--- a valid directory path.
|
||||
MiniMisc.find_root = function(buf_id, names, fallback)
|
||||
buf_id = buf_id or 0
|
||||
names = names or { '.git', 'Makefile' }
|
||||
fallback = fallback or function() return nil end
|
||||
|
||||
if type(buf_id) ~= 'number' then H.error('Argument `buf_id` of `find_root()` should be number.') end
|
||||
if not (H.is_array_of(names, H.is_string) or vim.is_callable(names)) then
|
||||
H.error('Argument `names` of `find_root()` should be array of string file names or a callable.')
|
||||
end
|
||||
if not vim.is_callable(fallback) then H.error('Argument `fallback` of `find_root()` should be callable.') end
|
||||
|
||||
-- Compute directory to start search from. NOTEs on why not using file path:
|
||||
-- - This has better performance because `vim.fs.find()` is called less.
|
||||
-- - *Needs* to be a directory for callable `names` to work.
|
||||
-- - Later search is done including initial `path` if directory, so this
|
||||
-- should work for detecting buffer directory as root.
|
||||
local path = vim.api.nvim_buf_get_name(buf_id)
|
||||
if path == '' then return end
|
||||
local dir_path = vim.fs.dirname(path)
|
||||
|
||||
-- Try using cache
|
||||
local res = H.root_cache[dir_path]
|
||||
if res ~= nil then return res end
|
||||
|
||||
-- Find root
|
||||
local root_file = vim.fs.find(names, { path = dir_path, upward = true })[1]
|
||||
if root_file ~= nil then
|
||||
res = vim.fs.dirname(root_file)
|
||||
else
|
||||
res = fallback(path)
|
||||
end
|
||||
|
||||
-- Use absolute path to an existing directory
|
||||
if type(res) ~= 'string' then return end
|
||||
res = vim.fn.fnamemodify(res, ':p')
|
||||
if vim.fn.isdirectory(res) == 0 then return end
|
||||
|
||||
-- Cache result per directory path
|
||||
H.root_cache[dir_path] = res
|
||||
|
||||
return res
|
||||
end
|
||||
|
||||
H.root_cache = {}
|
||||
|
||||
--- Set up terminal background synchronization
|
||||
---
|
||||
--- What it does:
|
||||
--- - Checks if terminal emulator supports OSC 11 control sequence. Stops if not.
|
||||
--- - Creates |UIEnter| and |ColorScheme| autocommands which change terminal
|
||||
--- background to have same color as |guibg| of |hl-Normal|.
|
||||
--- - Creates |UILeave| autocommand which sets terminal background back to the
|
||||
--- color at the time this function was called first time in current session.
|
||||
--- - Synchronizes background immediately to allow not depend on loading order.
|
||||
---
|
||||
--- Primary use case is to remove possible "frame" around current Neovim instance
|
||||
--- which appears if Neovim's |hl-Normal| background color differs from what is
|
||||
--- used by terminal emulator itself.
|
||||
---
|
||||
--- Make sure to call it only during interactive session in terminal emulator.
|
||||
MiniMisc.setup_termbg_sync = function()
|
||||
local augroup = vim.api.nvim_create_augroup('MiniMiscTermbgSync', { clear = true })
|
||||
local f = function(args)
|
||||
local ok, bg_init = pcall(H.parse_osc11, args.data)
|
||||
if not (ok and type(bg_init) == 'string') then
|
||||
H.notify('`setup_termbg_sync()` could not parse terminal emulator response ' .. vim.inspect(args.data), 'WARN')
|
||||
return
|
||||
end
|
||||
|
||||
-- Set up sync
|
||||
local sync = function()
|
||||
local normal = vim.api.nvim_get_hl_by_name('Normal', true)
|
||||
if normal.background == nil then return end
|
||||
io.write(string.format('\027]11;#%06x\007', normal.background))
|
||||
end
|
||||
vim.api.nvim_create_autocmd({ 'UIEnter', 'ColorScheme' }, { group = augroup, callback = sync })
|
||||
|
||||
-- Set up reset to the color returned from the very first call
|
||||
H.termbg_init = H.termbg_init or bg_init
|
||||
local reset = function() io.write('\027]11;' .. H.termbg_init .. '\007') end
|
||||
vim.api.nvim_create_autocmd({ 'UILeave' }, { group = augroup, callback = reset })
|
||||
|
||||
-- Sync immediately
|
||||
sync()
|
||||
end
|
||||
|
||||
-- Ask about current background color and process the response
|
||||
local id = vim.api.nvim_create_autocmd('TermResponse', { group = augroup, callback = f, once = true, nested = true })
|
||||
io.write('\027]11;?\007')
|
||||
vim.defer_fn(function()
|
||||
local ok = pcall(vim.api.nvim_del_autocmd, id)
|
||||
if ok then H.notify('`setup_termbg_sync()` did not get response from terminal emulator', 'WARN') end
|
||||
end, 1000)
|
||||
end
|
||||
|
||||
-- Source: 'runtime/lua/vim/_defaults.lua' in Neovim source
|
||||
H.parse_osc11 = function(x)
|
||||
local r, g, b = x:match('^\027%]11;rgb:(%x+)/(%x+)/(%x+)$')
|
||||
if not (r and g and b) then
|
||||
local a
|
||||
r, g, b, a = x:match('^\027%]11;rgba:(%x+)/(%x+)/(%x+)/(%x+)$')
|
||||
if not (a and a:len() <= 4) then return end
|
||||
end
|
||||
if not (r and g and b) then return end
|
||||
if not (r:len() <= 4 and g:len() <= 4 and b:len() <= 4) then return end
|
||||
local parse_osc_hex = function(c) return c:len() == 1 and (c .. c) or c:sub(1, 2) end
|
||||
return '#' .. parse_osc_hex(r) .. parse_osc_hex(g) .. parse_osc_hex(b)
|
||||
end
|
||||
|
||||
--- Restore cursor position on file open
|
||||
---
|
||||
--- When reopening a file this will make sure the cursor is placed back to the
|
||||
--- position where you left before. This implements |restore-cursor| in a nicer way.
|
||||
--- File should have a recognized file type (see 'filetype') and be opened in
|
||||
--- a normal buffer (see 'buftype').
|
||||
---
|
||||
--- Note: it relies on file mark data stored in 'shadafile' (see |shada-f|).
|
||||
--- Be sure to enable it.
|
||||
---
|
||||
---@param opts table|nil Options for |MiniMisc.restore_cursor|. Possible fields:
|
||||
--- - <center> - (boolean) Center the window after we restored the cursor.
|
||||
--- Default: `true`.
|
||||
--- - <ignore_filetype> - Array with file types to be ignored (see 'filetype').
|
||||
--- Default: `{ "gitcommit", "gitrebase" }`.
|
||||
---
|
||||
---@usage >lua
|
||||
--- require('mini.misc').setup_restore_cursor()
|
||||
--- <
|
||||
MiniMisc.setup_restore_cursor = function(opts)
|
||||
opts = opts or {}
|
||||
|
||||
opts.ignore_filetype = opts.ignore_filetype or { 'gitcommit', 'gitrebase' }
|
||||
if not H.is_array_of(opts.ignore_filetype, H.is_string) then
|
||||
H.error('In `setup_restore_cursor()` `opts.ignore_filetype` should be an array of strings.')
|
||||
end
|
||||
|
||||
if opts.center == nil then opts.center = true end
|
||||
if type(opts.center) ~= 'boolean' then H.error('In `setup_restore_cursor()` `opts.center` should be a boolean.') end
|
||||
|
||||
-- Create autocommand which runs once on `FileType` for every new buffer
|
||||
local augroup = vim.api.nvim_create_augroup('MiniMiscRestoreCursor', {})
|
||||
vim.api.nvim_create_autocmd('BufReadPre', {
|
||||
group = augroup,
|
||||
callback = function(data)
|
||||
vim.api.nvim_create_autocmd('FileType', {
|
||||
buffer = data.buf,
|
||||
once = true,
|
||||
callback = function() H.restore_cursor(opts) end,
|
||||
})
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
H.restore_cursor = function(opts)
|
||||
-- Stop if not a normal buffer
|
||||
if vim.bo.buftype ~= '' then return end
|
||||
|
||||
-- Stop if filetype is ignored
|
||||
if vim.tbl_contains(opts.ignore_filetype, vim.bo.filetype) then return end
|
||||
|
||||
-- Stop if line is already specified (like during start with `nvim file +num`)
|
||||
local cursor_line = vim.api.nvim_win_get_cursor(0)[1]
|
||||
if cursor_line > 1 then return end
|
||||
|
||||
-- Stop if can't restore proper line for some reason
|
||||
local mark_line = vim.api.nvim_buf_get_mark(0, [["]])[1]
|
||||
local n_lines = vim.api.nvim_buf_line_count(0)
|
||||
if not (1 <= mark_line and mark_line <= n_lines) then return end
|
||||
|
||||
-- Restore cursor and open just enough folds
|
||||
vim.cmd([[normal! g`"zv]])
|
||||
|
||||
-- Center window
|
||||
if opts.center then vim.cmd('normal! zz') end
|
||||
end
|
||||
|
||||
--- Compute summary statistics of numerical array
|
||||
---
|
||||
--- This might be useful to compute summary of time benchmarking with
|
||||
--- |MiniMisc.bench_time|.
|
||||
---
|
||||
---@param t table Array (table suitable for `ipairs`) of numbers.
|
||||
---
|
||||
---@return table Table with summary values under following keys (may be
|
||||
--- extended in the future): <maximum>, <mean>, <median>, <minimum>, <n>
|
||||
--- (number of elements), <sd> (sample standard deviation).
|
||||
MiniMisc.stat_summary = function(t)
|
||||
if not H.is_array_of(t, H.is_number) then
|
||||
H.error('Input of `MiniMisc.stat_summary()` should be an array of numbers.')
|
||||
end
|
||||
|
||||
-- Welford algorithm of computing variance
|
||||
-- Source: https://www.johndcook.com/blog/skewness_kurtosis/
|
||||
local n = #t
|
||||
local delta, m1, m2 = 0, 0, 0
|
||||
local minimum, maximum = math.huge, -math.huge
|
||||
for i, x in ipairs(t) do
|
||||
delta = x - m1
|
||||
m1 = m1 + delta / i
|
||||
m2 = m2 + delta * (x - m1)
|
||||
|
||||
-- Extremums
|
||||
minimum = x < minimum and x or minimum
|
||||
maximum = x > maximum and x or maximum
|
||||
end
|
||||
|
||||
return {
|
||||
maximum = maximum,
|
||||
mean = m1,
|
||||
median = H.compute_median(t),
|
||||
minimum = minimum,
|
||||
n = n,
|
||||
sd = math.sqrt(n > 1 and m2 / (n - 1) or 0),
|
||||
}
|
||||
end
|
||||
|
||||
H.compute_median = function(t)
|
||||
local n = #t
|
||||
if n == 0 then return 0 end
|
||||
|
||||
local t_sorted = vim.deepcopy(t)
|
||||
table.sort(t_sorted)
|
||||
return 0.5 * (t_sorted[math.ceil(0.5 * n)] + t_sorted[math.ceil(0.5 * (n + 1))])
|
||||
end
|
||||
|
||||
--- Return "first" elements of table as decided by `pairs`
|
||||
---
|
||||
--- Note: order of elements might vary.
|
||||
---
|
||||
---@param t table Input table.
|
||||
---@param n number|nil Maximum number of first elements. Default: 5.
|
||||
---
|
||||
---@return table Table with at most `n` first elements of `t` (with same keys).
|
||||
MiniMisc.tbl_head = function(t, n)
|
||||
n = n or 5
|
||||
local res, n_res = {}, 0
|
||||
for k, val in pairs(t) do
|
||||
if n_res >= n then return res end
|
||||
res[k] = val
|
||||
n_res = n_res + 1
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
--- Return "last" elements of table as decided by `pairs`
|
||||
---
|
||||
--- This function makes two passes through elements of `t`:
|
||||
--- - First to count number of elements.
|
||||
--- - Second to construct result.
|
||||
---
|
||||
--- Note: order of elements might vary.
|
||||
---
|
||||
---@param t table Input table.
|
||||
---@param n number|nil Maximum number of last elements. Default: 5.
|
||||
---
|
||||
---@return table Table with at most `n` last elements of `t` (with same keys).
|
||||
MiniMisc.tbl_tail = function(t, n)
|
||||
n = n or 5
|
||||
|
||||
-- Count number of elements on first pass
|
||||
local n_all = 0
|
||||
for _, _ in pairs(t) do
|
||||
n_all = n_all + 1
|
||||
end
|
||||
|
||||
-- Construct result on second pass
|
||||
local res = {}
|
||||
local i, start_i = 0, n_all - n + 1
|
||||
for k, val in pairs(t) do
|
||||
i = i + 1
|
||||
if i >= start_i then res[k] = val end
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
--- Add possibility of nested comment leader
|
||||
---
|
||||
--- This works by parsing 'commentstring' buffer option, extracting
|
||||
--- non-whitespace comment leader (symbols on the left of commented line), and
|
||||
--- locally modifying 'comments' option (by prepending `n:<leader>`). Does
|
||||
--- nothing if 'commentstring' is empty or has comment symbols both in front
|
||||
--- and back (like "/*%s*/").
|
||||
---
|
||||
--- Nested comment leader added with this function is useful for formatting
|
||||
--- nested comments. For example, have in Lua "first-level" comments with '--'
|
||||
--- and "second-level" comments with '----'. With nested comment leader second
|
||||
--- type can be formatted with `gq` in the same way as first one.
|
||||
---
|
||||
--- Recommended usage is with |autocmd|: >lua
|
||||
---
|
||||
--- local use_nested_comments = function() MiniMisc.use_nested_comments() end
|
||||
--- vim.api.nvim_create_autocmd('BufEnter', { callback = use_nested_comments })
|
||||
--- <
|
||||
--- Note: for most filetypes 'commentstring' option is added only when buffer
|
||||
--- with this filetype is entered, so using non-current `buf_id` can not lead
|
||||
--- to desired effect.
|
||||
---
|
||||
---@param buf_id number|nil Buffer identifier (see |bufnr()|) in which function
|
||||
--- will operate. Default: 0 for current.
|
||||
MiniMisc.use_nested_comments = function(buf_id)
|
||||
buf_id = buf_id or 0
|
||||
|
||||
local commentstring = vim.bo[buf_id].commentstring
|
||||
if commentstring == '' then return end
|
||||
|
||||
-- Extract raw comment leader from 'commentstring' option
|
||||
local comment_parts = vim.tbl_filter(function(x) return x ~= '' end, vim.split(commentstring, '%s', true))
|
||||
|
||||
-- Don't do anything if 'commentstring' is like '/*%s*/' (as in 'json')
|
||||
if #comment_parts > 1 then return end
|
||||
|
||||
-- Get comment leader by removing whitespace
|
||||
local leader = vim.trim(comment_parts[1])
|
||||
|
||||
local comments = vim.bo[buf_id].comments
|
||||
local new_comments = string.format('n:%s,%s', leader, comments)
|
||||
vim.api.nvim_buf_set_option(buf_id, 'comments', new_comments)
|
||||
end
|
||||
|
||||
--- Zoom in and out of a buffer, making it full screen in a floating window
|
||||
---
|
||||
--- This function is useful when working with multiple windows but temporarily
|
||||
--- needing to zoom into one to see more of the code from that buffer. Call it
|
||||
--- again (without arguments) to zoom out.
|
||||
---
|
||||
---@param buf_id number|nil Buffer identifier (see |bufnr()|) to be zoomed.
|
||||
--- Default: 0 for current.
|
||||
---@param config table|nil Optional config for window (as for |nvim_open_win()|).
|
||||
MiniMisc.zoom = function(buf_id, config)
|
||||
if H.zoom_winid and vim.api.nvim_win_is_valid(H.zoom_winid) then
|
||||
vim.api.nvim_win_close(H.zoom_winid, true)
|
||||
H.zoom_winid = nil
|
||||
else
|
||||
buf_id = buf_id or 0
|
||||
-- Currently very big `width` and `height` get truncated to maximum allowed
|
||||
local default_config = { relative = 'editor', row = 0, col = 0, width = 1000, height = 1000 }
|
||||
config = vim.tbl_deep_extend('force', default_config, config or {})
|
||||
H.zoom_winid = vim.api.nvim_open_win(buf_id, true, config)
|
||||
vim.wo.winblend = 0
|
||||
vim.cmd('normal! zz')
|
||||
end
|
||||
end
|
||||
|
||||
-- Helper data ================================================================
|
||||
-- Module default config
|
||||
H.default_config = vim.deepcopy(MiniMisc.config)
|
||||
|
||||
-- Window identifier of current zoom (for `zoom()`)
|
||||
H.zoom_winid = nil
|
||||
|
||||
-- Helper functionality =======================================================
|
||||
-- Settings -------------------------------------------------------------------
|
||||
H.setup_config = function(config)
|
||||
-- General idea: if some table elements are not present in user-supplied
|
||||
-- `config`, take them from default config
|
||||
vim.validate({ config = { config, 'table', true } })
|
||||
config = vim.tbl_deep_extend('force', vim.deepcopy(H.default_config), config or {})
|
||||
|
||||
vim.validate({
|
||||
make_global = {
|
||||
config.make_global,
|
||||
function(x)
|
||||
if type(x) ~= 'table' then return false end
|
||||
local present_fields = vim.tbl_keys(MiniMisc)
|
||||
for _, v in pairs(x) do
|
||||
if not vim.tbl_contains(present_fields, v) then return false end
|
||||
end
|
||||
return true
|
||||
end,
|
||||
'`make_global` should be a table with `MiniMisc` actual fields',
|
||||
},
|
||||
})
|
||||
|
||||
return config
|
||||
end
|
||||
|
||||
H.apply_config = function(config)
|
||||
MiniMisc.config = config
|
||||
|
||||
for _, v in pairs(config.make_global) do
|
||||
_G[v] = MiniMisc[v]
|
||||
end
|
||||
end
|
||||
|
||||
-- Utilities ------------------------------------------------------------------
|
||||
H.error = function(msg) error('(mini.misc) ' .. msg) end
|
||||
|
||||
H.notify = function(msg, level) vim.notify('(mini.misc) ' .. msg, vim.log.levels[level]) end
|
||||
|
||||
H.is_array_of = function(x, predicate)
|
||||
if not H.islist(x) then return false end
|
||||
for _, v in ipairs(x) do
|
||||
if not predicate(v) then return false end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
H.is_number = function(x) return type(x) == 'number' end
|
||||
|
||||
H.is_string = function(x) return type(x) == 'string' end
|
||||
|
||||
-- TODO: Remove after compatibility with Neovim=0.9 is dropped
|
||||
H.islist = vim.fn.has('nvim-0.10') == 1 and vim.islist or vim.tbl_islist
|
||||
|
||||
return MiniMisc
|
||||
496
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/move.lua
Normal file
496
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/move.lua
Normal file
@ -0,0 +1,496 @@
|
||||
--- *mini.move* Move any selection in any direction
|
||||
--- *MiniMove*
|
||||
---
|
||||
--- MIT License Copyright (c) 2023 Evgeni Chasnovski
|
||||
---
|
||||
--- ==============================================================================
|
||||
---
|
||||
--- Features:
|
||||
--- - Works in two modes:
|
||||
--- - Visual mode. Select text (charwise with |v|, linewise with |V|, and
|
||||
--- blockwise with |CTRL-V|) and press customizable mapping to move in
|
||||
--- all four directions (left, right, down, up). It keeps Visual mode.
|
||||
--- - Normal mode. Press customizable mapping to move current line in all
|
||||
--- four directions (left, right, down, up).
|
||||
--- - Special handling of linewise movement:
|
||||
--- - Vertical movement gets reindented with |=|.
|
||||
--- - Horizontal movement is improved indent/dedent with |>| / |<|.
|
||||
--- - Cursor moves along with selection.
|
||||
---
|
||||
--- - Provides both mappings and Lua functions for motions. See
|
||||
--- |MiniMove.move_selection()| and |MiniMove.move_line()|.
|
||||
---
|
||||
--- - Respects |v:count|. Movement mappings can be preceded by a number which
|
||||
--- multiplies command effect.
|
||||
---
|
||||
--- - All consecutive moves (regardless of direction) can be undone by a single |u|.
|
||||
---
|
||||
--- - Respects preferred column for vertical movement. It will vertically move
|
||||
--- selection as how cursor is moving (not strictly vertically if target
|
||||
--- column is not present in target line).
|
||||
---
|
||||
--- Notes:
|
||||
--- - Doesn't allow moving selection outside of current lines (by design).
|
||||
---
|
||||
--- # Setup ~
|
||||
---
|
||||
--- This module needs a setup with `require('mini.move').setup({})` (replace
|
||||
--- `{}` with your `config` table). It will create global Lua table `MiniMove`
|
||||
--- which you can use for scripting or manually (with `:lua MiniMove.*`).
|
||||
---
|
||||
--- See |MiniMove.config| for available config settings.
|
||||
---
|
||||
--- You can override runtime config settings (but not `config.mappings`) locally
|
||||
--- to buffer inside `vim.b.minimove_config` which should have same structure
|
||||
--- as `MiniMove.config`. See |mini.nvim-buffer-local-config| for more details.
|
||||
---
|
||||
--- # Comparisons ~
|
||||
---
|
||||
--- - 'matze/vim-move':
|
||||
--- - Doesn't support vertical movement of charwise and blockwise selections.
|
||||
--- While 'mini.move' does.
|
||||
--- - Doesn't support horizontal movement of current line in favor of
|
||||
--- horizontal movement of current character. While 'mini.move' supports
|
||||
--- horizontal movement of current line and doesn't support such movement
|
||||
--- of current character.
|
||||
--- - Has extra functionality for certain moves (like move by half page).
|
||||
--- While 'mini.move' does not (by design).
|
||||
--- - 'booperlv/nvim-gomove':
|
||||
--- - Doesn't support movement in charwise visual selection.
|
||||
--- While 'mini.move' does.
|
||||
--- - Has extra functionality beyond moving text, like duplication.
|
||||
--- While 'mini.move' concentrates only on moving functionality.
|
||||
---
|
||||
--- # Disabling ~
|
||||
---
|
||||
--- To disable, set `vim.g.minimove_disable` (globally) or `vim.b.minimove_disable`
|
||||
--- (for a buffer) to `true`. Considering high number of different scenarios
|
||||
--- and customization intentions, writing exact rules for disabling module's
|
||||
--- functionality is left to user. See |mini.nvim-disabling-recipes| for common
|
||||
--- recipes.
|
||||
|
||||
---@alias __move_direction string One of "left", "down", "up", "right".
|
||||
---@alias __move_opts table|nil Options. Same structure as `options` in |MiniMove.config|
|
||||
--- (with its values as defaults) plus these allowed extra fields:
|
||||
--- - <n_times> (number) - number of times to try to make a move.
|
||||
--- Default: |v:count1|.
|
||||
|
||||
---@diagnostic disable:undefined-field
|
||||
|
||||
-- Module definition ==========================================================
|
||||
local MiniMove = {}
|
||||
local H = {}
|
||||
|
||||
--- Module setup
|
||||
---
|
||||
---@param config table|nil Module config table. See |MiniMove.config|.
|
||||
---
|
||||
---@usage >lua
|
||||
--- require('mini.move').setup() -- use default config
|
||||
--- -- OR
|
||||
--- require('mini.move').setup({}) -- replace {} with your config table
|
||||
--- <
|
||||
MiniMove.setup = function(config)
|
||||
-- Export module
|
||||
_G.MiniMove = MiniMove
|
||||
|
||||
-- Setup config
|
||||
config = H.setup_config(config)
|
||||
|
||||
-- Apply config
|
||||
H.apply_config(config)
|
||||
end
|
||||
|
||||
--- Module config
|
||||
---
|
||||
--- Default values:
|
||||
---@eval return MiniDoc.afterlines_to_code(MiniDoc.current.eval_section)
|
||||
---@text # Mappings ~
|
||||
---
|
||||
--- Other possible choices of mappings: >lua
|
||||
---
|
||||
--- -- `HJKL` for moving visual selection (overrides H, L, J in Visual mode)
|
||||
--- require('mini.move').setup({
|
||||
--- mappings = {
|
||||
--- left = 'H',
|
||||
--- right = 'L',
|
||||
--- down = 'J',
|
||||
--- up = 'K',
|
||||
--- }
|
||||
--- })
|
||||
---
|
||||
--- -- Shift + arrows
|
||||
--- require('mini.move').setup({
|
||||
--- mappings = {
|
||||
--- left = '<S-left>',
|
||||
--- right = '<S-right>',
|
||||
--- down = '<S-down>',
|
||||
--- up = '<S-up>',
|
||||
---
|
||||
--- line_left = '<S-left>',
|
||||
--- line_right = '<S-right>',
|
||||
--- line_down = '<S-down>',
|
||||
--- line_up = '<S-up>',
|
||||
--- }
|
||||
--- })
|
||||
--- <
|
||||
MiniMove.config = {
|
||||
-- Module mappings. Use `''` (empty string) to disable one.
|
||||
mappings = {
|
||||
-- Move visual selection in Visual mode. Defaults are Alt (Meta) + hjkl.
|
||||
left = '<M-h>',
|
||||
right = '<M-l>',
|
||||
down = '<M-j>',
|
||||
up = '<M-k>',
|
||||
|
||||
-- Move current line in Normal mode
|
||||
line_left = '<M-h>',
|
||||
line_right = '<M-l>',
|
||||
line_down = '<M-j>',
|
||||
line_up = '<M-k>',
|
||||
},
|
||||
|
||||
-- Options which control moving behavior
|
||||
options = {
|
||||
-- Automatically reindent selection during linewise vertical move
|
||||
reindent_linewise = true,
|
||||
},
|
||||
}
|
||||
--minidoc_afterlines_end
|
||||
|
||||
-- Module functionality =======================================================
|
||||
--- Move visually selected region in any direction within present lines
|
||||
---
|
||||
--- Main function powering visual selection move in Visual mode.
|
||||
---
|
||||
--- Notes:
|
||||
--- - Vertical movement in linewise mode is followed up by reindent with |v_=|.
|
||||
--- - Horizontal movement in linewise mode is same as |v_<| and |v_>|.
|
||||
---
|
||||
---@param direction __move_direction
|
||||
---@param opts __move_opts
|
||||
MiniMove.move_selection = function(direction, opts)
|
||||
if H.is_disabled() or not vim.o.modifiable then return end
|
||||
|
||||
opts = vim.tbl_deep_extend('force', H.get_config().options, opts or {})
|
||||
|
||||
-- This could have been a one-line expression mappings, but there are issues:
|
||||
-- - Initial yanking modifies some register. Not critical, but also not good.
|
||||
-- - Doesn't work at movement edges (first line for `K`, etc.). See
|
||||
-- https://github.com/vim/vim/issues/11786
|
||||
-- - Results into each movement being a separate undo block, which is
|
||||
-- inconvenient with several back-to-back movements.
|
||||
local cur_mode = vim.fn.mode()
|
||||
|
||||
-- Act only inside visual mode
|
||||
if not (cur_mode == 'v' or cur_mode == 'V' or cur_mode == '\22') then return end
|
||||
|
||||
-- Define common predicates
|
||||
local dir_type = (direction == 'up' or direction == 'down') and 'vert' or 'hori'
|
||||
local is_linewise = cur_mode == 'V'
|
||||
|
||||
-- Cache useful data because it will be reset when executing commands
|
||||
local n_times = opts.n_times or vim.v.count1
|
||||
local ref_curpos, ref_last_col = vim.fn.getcurpos(), vim.fn.col('$')
|
||||
local is_cursor_on_selection_start = vim.fn.line('.') < vim.fn.line('v')
|
||||
|
||||
-- Determine if previous action was this type of move
|
||||
local is_moving = vim.deep_equal(H.state, H.get_move_state())
|
||||
if not is_moving then H.curswant = nil end
|
||||
|
||||
-- Allow undo of consecutive moves at once (direction doesn't matter)
|
||||
local cmd = H.make_cmd_normal(is_moving)
|
||||
|
||||
-- Treat horizontal linewise movement specially
|
||||
if is_linewise and dir_type == 'hori' then
|
||||
-- Use indentation as horizontal movement for linewise selection
|
||||
cmd(n_times .. H.indent_keys[direction] .. 'gv')
|
||||
|
||||
-- Make cursor move along selection
|
||||
H.correct_cursor_col(ref_curpos, ref_last_col)
|
||||
|
||||
-- Track new state to allow joining in single undo block
|
||||
H.state = H.get_move_state()
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
-- Temporarily ensure possibility to put cursor just after line end.
|
||||
-- This allows a more intuitive cursor positioning from and to end of line.
|
||||
-- NOTE: somehow, this should be done before initial cut to take effect.
|
||||
local cache_virtualedit = vim.o.virtualedit
|
||||
if not cache_virtualedit:find('all') then vim.o.virtualedit = 'onemore' end
|
||||
|
||||
-- Cut selection while saving caching register
|
||||
local cache_z_reg = vim.fn.getreg('z')
|
||||
cmd('"zx')
|
||||
|
||||
-- Detect edge selection: last line(s) for vertical and last character(s)
|
||||
-- for horizontal. At this point (after cutting selection) cursor is on the
|
||||
-- edge which can happen in two cases:
|
||||
-- - Move second to last selection towards edge (like in 'abc' move 'b'
|
||||
-- to right or second to last line down).
|
||||
-- - Move edge selection away from edge (like in 'abc' move 'c' to left
|
||||
-- or last line up).
|
||||
-- Use condition that removed selection was further than current cursor
|
||||
-- to distinguish between two cases.
|
||||
local is_edge_selection_hori = dir_type == 'hori' and vim.fn.col('.') < vim.fn.col("'<")
|
||||
local is_edge_selection_vert = dir_type == 'vert' and vim.fn.line('.') < vim.fn.line("'<")
|
||||
local is_edge_selection = is_edge_selection_hori or is_edge_selection_vert
|
||||
|
||||
-- Use `p` as paste key instead of `P` in cases which might require moving
|
||||
-- selection to place which is unreachable with `P`: right to be line end
|
||||
-- and down to be last line. NOTE: temporary `virtualedit=onemore` solves
|
||||
-- this only for horizontal movement, but not for vertical.
|
||||
local can_go_overline = not is_linewise and direction == 'right'
|
||||
local can_go_overbuf = is_linewise and direction == 'down'
|
||||
local paste_key = (can_go_overline or can_go_overbuf) and 'p' or 'P'
|
||||
|
||||
-- Restore `curswant` to try move cursor to initial column (just like
|
||||
-- default `hjkl` moves)
|
||||
if dir_type == 'vert' then H.set_curswant(H.curswant) end
|
||||
|
||||
-- Possibly reduce number of moves by one to not overshoot move
|
||||
local n = n_times - ((paste_key == 'p' or is_edge_selection) and 1 or 0)
|
||||
|
||||
-- Don't allow movement past last line of block selection (any part)
|
||||
if cur_mode == '\22' and direction == 'down' and vim.fn.line('$') == vim.fn.line("'>") then n = 0 end
|
||||
|
||||
-- Move cursor
|
||||
if n > 0 then cmd(n .. H.move_keys[direction]) end
|
||||
|
||||
-- Save curswant. Correct for one less move when using `p` as paste.
|
||||
H.curswant = H.get_curswant() + ((direction == 'right' and paste_key == 'p') and 1 or 0)
|
||||
|
||||
-- Open just enough folds (but not in linewise mode, as it allows moving
|
||||
-- past folds)
|
||||
if not is_linewise then cmd('zv') end
|
||||
|
||||
-- Paste
|
||||
cmd('"z' .. paste_key)
|
||||
|
||||
-- Select newly moved region. Another way is to use something like `gvhoho`
|
||||
-- but it doesn't work well with selections spanning several lines.
|
||||
cmd('`[1v')
|
||||
|
||||
-- Do extra in case of linewise selection
|
||||
if is_linewise then
|
||||
-- Reindent linewise selection if `=` can do that.
|
||||
-- NOTE: this sometimes doesn't work well with folds (and probably
|
||||
-- `foldmethod=indent`) and linewise mode because it recomputes folds after
|
||||
-- that and the whole "move past fold" doesn't work.
|
||||
if opts.reindent_linewise and dir_type == 'vert' and vim.o.equalprg == '' then cmd('=gv') end
|
||||
|
||||
-- Move cursor along the selection. NOTE: do this *after* reindent to
|
||||
-- account for its effect.
|
||||
-- - Ensure that cursor is on the right side of selection
|
||||
if is_cursor_on_selection_start then cmd('o') end
|
||||
H.correct_cursor_col(ref_curpos, ref_last_col)
|
||||
end
|
||||
|
||||
-- Restore intermediate values
|
||||
vim.fn.setreg('z', cache_z_reg)
|
||||
vim.o.virtualedit = cache_virtualedit
|
||||
|
||||
-- Track new state to allow joining in single undo block
|
||||
H.state = H.get_move_state()
|
||||
end
|
||||
|
||||
--- Move current line in any direction
|
||||
---
|
||||
--- Main function powering current line move in Normal mode.
|
||||
---
|
||||
--- Notes:
|
||||
--- - Vertical movement is followed up by reindent with |v_=|.
|
||||
--- - Horizontal movement is almost the same as |<<| and |>>| with a different
|
||||
--- handling of |v:count| (multiplies shift effect instead of modifying that
|
||||
--- number of lines).
|
||||
---
|
||||
---@param direction __move_direction
|
||||
---@param opts __move_opts
|
||||
MiniMove.move_line = function(direction, opts)
|
||||
if H.is_disabled() or not vim.o.modifiable then return end
|
||||
|
||||
opts = vim.tbl_deep_extend('force', H.get_config().options, opts or {})
|
||||
|
||||
-- Determine if previous action was this type of move
|
||||
local is_moving = vim.deep_equal(H.state, H.get_move_state())
|
||||
|
||||
-- Allow undo of consecutive moves at once (direction doesn't matter)
|
||||
local cmd = H.make_cmd_normal(is_moving)
|
||||
|
||||
-- Cache useful data because it will be reset when executing commands
|
||||
local n_times = opts.n_times or vim.v.count1
|
||||
local is_last_line_up = direction == 'up' and vim.fn.line('.') == vim.fn.line('$')
|
||||
local ref_curpos, ref_last_col = vim.fn.getcurpos(), vim.fn.col('$')
|
||||
|
||||
if direction == 'left' or direction == 'right' then
|
||||
-- Use indentation as horizontal movement. Explicitly call `count1` because
|
||||
-- `<`/`>` use `v:count` to define number of lines.
|
||||
-- Go to first non-blank at the end.
|
||||
local key = H.indent_keys[direction]
|
||||
cmd(string.rep(key .. key, n_times))
|
||||
|
||||
-- Make cursor move along selection
|
||||
H.correct_cursor_col(ref_curpos, ref_last_col)
|
||||
|
||||
-- Track new state to allow joining in single undo block
|
||||
H.state = H.get_move_state()
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
-- Cut curre lint while saving caching register
|
||||
local cache_z_reg = vim.fn.getreg('z')
|
||||
cmd('"zdd')
|
||||
|
||||
-- Move cursor
|
||||
local paste_key = direction == 'up' and 'P' or 'p'
|
||||
local n = n_times - ((paste_key == 'p' or is_last_line_up) and 1 or 0)
|
||||
if n > 0 then cmd(n .. H.move_keys[direction]) end
|
||||
|
||||
-- Paste
|
||||
cmd('"z' .. paste_key)
|
||||
|
||||
-- Reindent and put cursor on first non-blank
|
||||
if opts.reindent_linewise and vim.o.equalprg == '' then cmd('==') end
|
||||
|
||||
-- Move cursor along the selection. NOTE: do this *after* reindent to
|
||||
-- account for its effect.
|
||||
H.correct_cursor_col(ref_curpos, ref_last_col)
|
||||
|
||||
-- Restore intermediate values
|
||||
vim.fn.setreg('z', cache_z_reg)
|
||||
|
||||
-- Track new state to allow joining in single undo block
|
||||
H.state = H.get_move_state()
|
||||
end
|
||||
|
||||
-- Helper data ================================================================
|
||||
-- Module default config
|
||||
H.default_config = vim.deepcopy(MiniMove.config)
|
||||
|
||||
H.move_keys = { left = 'h', down = 'j', up = 'k', right = 'l' }
|
||||
H.indent_keys = { left = '<', right = '>' }
|
||||
|
||||
-- Moving state used to decide when to start new undo block ...
|
||||
H.state = {
|
||||
-- ... on buffer change
|
||||
buf_id = nil,
|
||||
-- ... on text change
|
||||
changedtick = nil,
|
||||
-- ... on cursor move
|
||||
cursor = nil,
|
||||
-- ... on mode change
|
||||
mode = nil,
|
||||
}
|
||||
|
||||
H.curswant = nil
|
||||
|
||||
-- Helper functionality =======================================================
|
||||
-- Settings -------------------------------------------------------------------
|
||||
H.setup_config = function(config)
|
||||
-- General idea: if some table elements are not present in user-supplied
|
||||
-- `config`, take them from default config
|
||||
vim.validate({ config = { config, 'table', true } })
|
||||
config = vim.tbl_deep_extend('force', vim.deepcopy(H.default_config), config or {})
|
||||
|
||||
vim.validate({
|
||||
mappings = { config.mappings, 'table' },
|
||||
options = { config.options, 'table' },
|
||||
})
|
||||
|
||||
vim.validate({
|
||||
['mappings.left'] = { config.mappings.left, 'string' },
|
||||
['mappings.down'] = { config.mappings.down, 'string' },
|
||||
['mappings.up'] = { config.mappings.up, 'string' },
|
||||
['mappings.right'] = { config.mappings.right, 'string' },
|
||||
|
||||
['mappings.line_left'] = { config.mappings.line_left, 'string' },
|
||||
['mappings.line_right'] = { config.mappings.line_right, 'string' },
|
||||
['mappings.line_down'] = { config.mappings.line_down, 'string' },
|
||||
['mappings.line_up'] = { config.mappings.line_up, 'string' },
|
||||
|
||||
['options.reindent_linewise'] = { config.options.reindent_linewise, 'boolean' },
|
||||
})
|
||||
|
||||
return config
|
||||
end
|
||||
|
||||
--stylua: ignore
|
||||
H.apply_config = function(config)
|
||||
MiniMove.config = config
|
||||
|
||||
-- Make mappings
|
||||
local maps = config.mappings
|
||||
|
||||
H.map('x', maps.left, [[<Cmd>lua MiniMove.move_selection('left')<CR>]], { desc = 'Move left' })
|
||||
H.map('x', maps.right, [[<Cmd>lua MiniMove.move_selection('right')<CR>]], { desc = 'Move right' })
|
||||
H.map('x', maps.down, [[<Cmd>lua MiniMove.move_selection('down')<CR>]], { desc = 'Move down' })
|
||||
H.map('x', maps.up, [[<Cmd>lua MiniMove.move_selection('up')<CR>]], { desc = 'Move up' })
|
||||
|
||||
H.map('n', maps.line_left, [[<Cmd>lua MiniMove.move_line('left')<CR>]], { desc = 'Move line left' })
|
||||
H.map('n', maps.line_right, [[<Cmd>lua MiniMove.move_line('right')<CR>]], { desc = 'Move line right' })
|
||||
H.map('n', maps.line_down, [[<Cmd>lua MiniMove.move_line('down')<CR>]], { desc = 'Move line down' })
|
||||
H.map('n', maps.line_up, [[<Cmd>lua MiniMove.move_line('up')<CR>]], { desc = 'Move line up' })
|
||||
end
|
||||
|
||||
H.is_disabled = function() return vim.g.minimove_disable == true or vim.b.minimove_disable == true end
|
||||
|
||||
H.get_config = function(config)
|
||||
return vim.tbl_deep_extend('force', MiniMove.config, vim.b.minimove_config or {}, config or {})
|
||||
end
|
||||
|
||||
-- Utilities ------------------------------------------------------------------
|
||||
H.map = function(mode, lhs, rhs, opts)
|
||||
if lhs == '' then return end
|
||||
opts = vim.tbl_deep_extend('force', { silent = true }, opts or {})
|
||||
vim.keymap.set(mode, lhs, rhs, opts)
|
||||
end
|
||||
|
||||
H.make_cmd_normal = function(include_undojoin)
|
||||
local normal_command = (include_undojoin and 'undojoin | ' or '') .. 'silent keepjumps normal! '
|
||||
|
||||
return function(x)
|
||||
-- Caching and restoring data on every command is not necessary but leads
|
||||
-- to a nicer implementation
|
||||
|
||||
-- Disable 'mini.bracketed' to avoid unwanted entries to its yank history
|
||||
local cache_minibracketed_disable = vim.b.minibracketed_disable
|
||||
local cache_unnamed_register = vim.fn.getreg('"')
|
||||
|
||||
-- Don't track possible put commands into yank history
|
||||
vim.b.minibracketed_disable = true
|
||||
|
||||
vim.cmd(normal_command .. x)
|
||||
|
||||
vim.b.minibracketed_disable = cache_minibracketed_disable
|
||||
vim.fn.setreg('"', cache_unnamed_register)
|
||||
end
|
||||
end
|
||||
|
||||
H.get_move_state = function()
|
||||
return {
|
||||
buf_id = vim.api.nvim_get_current_buf(),
|
||||
changedtick = vim.b.changedtick,
|
||||
cursor = vim.api.nvim_win_get_cursor(0),
|
||||
mode = vim.fn.mode(),
|
||||
}
|
||||
end
|
||||
|
||||
H.correct_cursor_col = function(ref_curpos, ref_last_col)
|
||||
-- Use `ref_curpos = getcurpos()` instead of `vim.api.nvim_win_get_cursor(0)`
|
||||
-- allows to also account for `virtualedit=all`
|
||||
|
||||
local col_diff = vim.fn.col('$') - ref_last_col
|
||||
local new_col = math.max(ref_curpos[3] + col_diff, 1)
|
||||
vim.fn.cursor({ vim.fn.line('.'), new_col, ref_curpos[4], ref_curpos[5] + col_diff })
|
||||
end
|
||||
|
||||
H.get_curswant = function() return vim.fn.winsaveview().curswant end
|
||||
H.set_curswant = function(x)
|
||||
if x == nil then return end
|
||||
vim.fn.winrestview({ curswant = x })
|
||||
end
|
||||
|
||||
return MiniMove
|
||||
845
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/notify.lua
Normal file
845
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/notify.lua
Normal file
@ -0,0 +1,845 @@
|
||||
--- *mini.notify* Show notifications
|
||||
--- *MiniNotify*
|
||||
---
|
||||
--- MIT License Copyright (c) 2024 Evgeni Chasnovski
|
||||
---
|
||||
--- ==============================================================================
|
||||
---
|
||||
--- Features:
|
||||
---
|
||||
--- - Show one or more highlighted notifications in a single floating window.
|
||||
---
|
||||
--- - Manage notifications (add, update, remove, clear).
|
||||
---
|
||||
--- - |vim.notify()| wrapper generator (see |MiniNotify.make_notify()|).
|
||||
---
|
||||
--- - Automated show of LSP progress report.
|
||||
---
|
||||
--- - Track history which can be accessed with |MiniNotify.get_all()|
|
||||
--- and shown with |MiniNotify.show_history()|.
|
||||
---
|
||||
--- # Setup ~
|
||||
---
|
||||
--- This module needs a setup with `require('mini.notify').setup({})` (replace
|
||||
--- `{}` with your `config` table). It will create global Lua table `MiniNotify`
|
||||
--- which you can use for scripting or manually (with `:lua MiniNotify.*`).
|
||||
---
|
||||
--- See |MiniNotify.config| for `config` structure and default values.
|
||||
---
|
||||
--- You can override runtime config settings locally to buffer inside
|
||||
--- `vim.b.mininotify_config` which should have same structure as
|
||||
--- `MiniNotify.config`. See |mini.nvim-buffer-local-config| for more details.
|
||||
---
|
||||
--- # Comparisons ~
|
||||
---
|
||||
--- - 'j-hui/fidget.nvim':
|
||||
--- - Basic goals of providing interface for notifications are similar.
|
||||
--- - Has more configuration options and visual effects, while this module
|
||||
--- does not (by design).
|
||||
---
|
||||
--- - 'rcarriga/nvim-notify':
|
||||
--- - Similar to 'j-hui/fidget.nvim'.
|
||||
---
|
||||
--- # Highlight groups ~
|
||||
---
|
||||
--- * `MiniNotifyBorder` - window border.
|
||||
--- * `MiniNotifyNormal` - basic foreground/background highlighting.
|
||||
--- * `MiniNotifyTitle` - window title.
|
||||
---
|
||||
--- To change any highlight group, modify it directly with |:highlight|.
|
||||
---
|
||||
--- # Disabling ~
|
||||
---
|
||||
--- To disable showing notifications, set `vim.g.mininotify_disable` (globally) or
|
||||
--- `vim.b.mininotify_disable` (for a buffer) to `true`. Considering high number
|
||||
--- of different scenarios and customization intentions, writing exact rules
|
||||
--- for disabling module's functionality is left to user. See
|
||||
--- |mini.nvim-disabling-recipes| for common recipes.
|
||||
|
||||
--- # Notification specification ~
|
||||
---
|
||||
--- Notification is a table with the following keys:
|
||||
---
|
||||
--- - <msg> `(string)` - single string with notification message.
|
||||
--- Use `\n` to delimit several lines.
|
||||
--- - <level> `(string)` - notification level as key of |vim.log.levels|.
|
||||
--- Like "ERROR", "WARN", "INFO", etc.
|
||||
--- - <hl_group> `(string)` - highlight group with which notification is shown.
|
||||
--- - <ts_add> `(number)` - timestamp of when notification is added.
|
||||
--- - <ts_update> `(number)` - timestamp of the latest notification update.
|
||||
--- - <ts_remove> `(number|nil)` - timestamp of when notification is removed.
|
||||
--- It is `nil` if notification was never removed and thus considered "active".
|
||||
---
|
||||
--- Notes:
|
||||
--- - Timestamps are compatible with |strftime()| and have fractional part.
|
||||
---@tag MiniNotify-specification
|
||||
|
||||
---@diagnostic disable:undefined-field
|
||||
---@diagnostic disable:discard-returns
|
||||
---@diagnostic disable:unused-local
|
||||
---@diagnostic disable:cast-local-type
|
||||
---@diagnostic disable:undefined-doc-name
|
||||
---@diagnostic disable:luadoc-miss-type-name
|
||||
|
||||
-- Module definition ==========================================================
|
||||
local MiniNotify = {}
|
||||
local H = {}
|
||||
|
||||
--- Module setup
|
||||
---
|
||||
---@param config table|nil Module config table. See |MiniNotify.config|.
|
||||
---
|
||||
---@usage >lua
|
||||
--- require('mini.notify').setup() -- use default config
|
||||
--- -- OR
|
||||
--- require('mini.notify').setup({}) -- replace {} with your config table
|
||||
--- <
|
||||
MiniNotify.setup = function(config)
|
||||
-- Export module
|
||||
_G.MiniNotify = MiniNotify
|
||||
|
||||
-- Setup config
|
||||
config = H.setup_config(config)
|
||||
|
||||
-- Apply config
|
||||
H.apply_config(config)
|
||||
|
||||
-- Define behavior
|
||||
H.create_autocommands(config)
|
||||
|
||||
-- Create default highlighting
|
||||
H.create_default_hl()
|
||||
end
|
||||
|
||||
--- Module config
|
||||
---
|
||||
--- Default values:
|
||||
---@eval return MiniDoc.afterlines_to_code(MiniDoc.current.eval_section)
|
||||
---@text # Content ~
|
||||
---
|
||||
--- `config.content` defines how notifications are shown.
|
||||
---
|
||||
--- `content.format` is a function which takes single notification object
|
||||
--- (see |MiniNotify-specification|) and returns a string to be used directly
|
||||
--- when showing notification.
|
||||
--- Default: `nil` for |MiniNotify.default_format()|.
|
||||
---
|
||||
--- `content.sort` is a function which takes array of notification objects
|
||||
--- (see |MiniNotify-specification|) and returns an array of such objects.
|
||||
--- It can be used to define custom order and/or filter for notifications which
|
||||
--- are shown simultaneously.
|
||||
--- Note: Input contains notifications before applying `content.format`.
|
||||
--- Default: `nil` for |MiniNotify.default_sort()|.
|
||||
---
|
||||
--- Example: >lua
|
||||
---
|
||||
--- require('mini.notify').setup({
|
||||
--- content = {
|
||||
--- -- Use notification message as is
|
||||
--- format = function(notif) return notif.msg end,
|
||||
---
|
||||
--- -- Show more recent notifications first
|
||||
--- sort = function(notif_arr)
|
||||
--- table.sort(
|
||||
--- notif_arr,
|
||||
--- function(a, b) return a.ts_update > b.ts_update end
|
||||
--- )
|
||||
--- return notif_arr
|
||||
--- end,
|
||||
--- },
|
||||
--- })
|
||||
--- <
|
||||
--- # LSP progress ~
|
||||
---
|
||||
--- `config.lsp_progress` defines automated notifications for LSP progress.
|
||||
--- It is implemented as a single updating notification with all information
|
||||
--- about the progress.
|
||||
--- Setting up is done inside |MiniNotify.setup()| via |vim.schedule()|'ed setting
|
||||
--- of |lsp-handler| for "$/progress" method.
|
||||
---
|
||||
--- `lsp_progress.enable` is a boolean indicating whether LSP progress should
|
||||
--- be shown in notifications. Can be disabled in current session.
|
||||
--- Default: `true`. Note: Should be `true` during |MiniNotify.setup()| call to be able
|
||||
--- to enable it in current session.
|
||||
---
|
||||
--- `lsp_progress.duration_last` is a number of milliseconds for the last progress
|
||||
--- report to be shown on screen before removing it.
|
||||
--- Default: 1000.
|
||||
---
|
||||
--- Notes:
|
||||
--- - This respects previously set handler by saving and calling it.
|
||||
--- - Overrding "$/progress" method of `vim.lsp.handlers` disables notifications.
|
||||
---
|
||||
--- # Window ~
|
||||
---
|
||||
--- `config.window` defines behavior of notification window.
|
||||
---
|
||||
--- `window.config` is a table defining floating window characteristics
|
||||
--- or a callable returning such table (will be called with identifier of
|
||||
--- window's buffer already showing notifications). It should have the same
|
||||
--- structure as in |nvim_open_win()|. It has the following default values
|
||||
--- which show notifications in the upper right corner with upper limit on width:
|
||||
--- - `width` is chosen to fit buffer content but at most `window.max_width_share`
|
||||
--- share of 'columns'.
|
||||
--- To have higher maximum width, use function in `config.window` which computes
|
||||
--- dimensions inside of it (based on buffer content).
|
||||
--- - `height` is chosen to fit buffer content with enabled 'wrap' (assuming
|
||||
--- default value of `width`).
|
||||
--- - `anchor`, `col`, and `row` are "NE", 'columns', and 0 or 1 (depending on tabline).
|
||||
--- - `border` is "single".
|
||||
--- - `zindex` is 999 to be as much on top as reasonably possible.
|
||||
---
|
||||
--- `window.max_width_share` defines maximum window width as a share of 'columns'.
|
||||
--- Should be a number between 0 (not included) and 1.
|
||||
--- Default: 0.382.
|
||||
---
|
||||
--- `window.winblend` defines 'winblend' value for notification window.
|
||||
--- Default: 25.
|
||||
MiniNotify.config = {
|
||||
-- Content management
|
||||
content = {
|
||||
-- Function which formats the notification message
|
||||
-- By default prepends message with notification time
|
||||
format = nil,
|
||||
|
||||
-- Function which orders notification array from most to least important
|
||||
-- By default orders first by level and then by update timestamp
|
||||
sort = nil,
|
||||
},
|
||||
|
||||
-- Notifications about LSP progress
|
||||
lsp_progress = {
|
||||
-- Whether to enable showing
|
||||
enable = true,
|
||||
|
||||
-- Duration (in ms) of how long last message should be shown
|
||||
duration_last = 1000,
|
||||
},
|
||||
|
||||
-- Window options
|
||||
window = {
|
||||
-- Floating window config
|
||||
config = {},
|
||||
|
||||
-- Maximum window width as share (between 0 and 1) of available columns
|
||||
max_width_share = 0.382,
|
||||
|
||||
-- Value of 'winblend' option
|
||||
winblend = 25,
|
||||
},
|
||||
}
|
||||
--minidoc_afterlines_end
|
||||
|
||||
--- Make vim.notify wrapper
|
||||
---
|
||||
--- Calling this function creates an implementation of |vim.notify()| powered
|
||||
--- by this module. General idea is that notification is shown right away (as
|
||||
--- soon as safely possible, see |vim.schedule()|) and removed after a configurable
|
||||
--- amount of time.
|
||||
---
|
||||
--- Examples: >lua
|
||||
---
|
||||
--- -- Defaults
|
||||
--- vim.notify = require('mini.notify').make_notify()
|
||||
---
|
||||
--- -- Change duration for errors to show them longer
|
||||
--- local opts = { ERROR = { duration = 10000 } }
|
||||
--- vim.notify = require('mini.notify').make_notify(opts)
|
||||
--- <
|
||||
---@param opts table|nil Options to configure behavior of notification `level`
|
||||
--- (as in |MiniNotfiy.add()|). Fields are the same as names of `vim.log.levels`
|
||||
--- with values being tables with possible fields:
|
||||
--- - <duration> `(number)` - duration (in ms) of how much a notification
|
||||
--- should be shown. If 0 or negative, notification is not shown at all.
|
||||
--- - <hl_group> `(string)` - highlight group of notification.
|
||||
--- Only data different to default can be supplied.
|
||||
---
|
||||
--- Default: >lua
|
||||
---
|
||||
--- {
|
||||
--- ERROR = { duration = 5000, hl_group = 'DiagnosticError' },
|
||||
--- WARN = { duration = 5000, hl_group = 'DiagnosticWarn' },
|
||||
--- INFO = { duration = 5000, hl_group = 'DiagnosticInfo' },
|
||||
--- DEBUG = { duration = 0, hl_group = 'DiagnosticHint' },
|
||||
--- TRACE = { duration = 0, hl_group = 'DiagnosticOk' },
|
||||
--- OFF = { duration = 0, hl_group = 'MiniNotifyNormal' },
|
||||
--- }
|
||||
--- <
|
||||
MiniNotify.make_notify = function(opts)
|
||||
local level_names = {}
|
||||
for k, v in pairs(vim.log.levels) do
|
||||
level_names[v] = k
|
||||
end
|
||||
|
||||
--stylua: ignore
|
||||
local default_opts = {
|
||||
ERROR = { duration = 5000, hl_group = 'DiagnosticError' },
|
||||
WARN = { duration = 5000, hl_group = 'DiagnosticWarn' },
|
||||
INFO = { duration = 5000, hl_group = 'DiagnosticInfo' },
|
||||
DEBUG = { duration = 0, hl_group = 'DiagnosticHint' },
|
||||
TRACE = { duration = 0, hl_group = 'DiagnosticOk' },
|
||||
OFF = { duration = 0, hl_group = 'MiniNotifyNormal' },
|
||||
}
|
||||
opts = vim.tbl_deep_extend('force', default_opts, opts or {})
|
||||
|
||||
for key, val in pairs(opts) do
|
||||
if default_opts[key] == nil then H.error('Keys should be log level names.') end
|
||||
if type(val) ~= 'table' then H.error('Level data should be table.') end
|
||||
if type(val.duration) ~= 'number' then H.error('`duration` in level data should be number.') end
|
||||
if type(val.hl_group) ~= 'string' then H.error('`hl_group` in level data should be string.') end
|
||||
end
|
||||
|
||||
-- Use `vim.schedule_wrap` for output to be usable inside `vim.uv` callbacks
|
||||
local notify = function(msg, level)
|
||||
level = level or vim.log.levels.INFO
|
||||
local level_name = level_names[level]
|
||||
if level_name == nil then H.error('Only valid values of `vim.log.levels` are supported.') end
|
||||
|
||||
local level_data = opts[level_name]
|
||||
if level_data.duration <= 0 then return end
|
||||
|
||||
local id = MiniNotify.add(msg, level_name, level_data.hl_group)
|
||||
vim.defer_fn(function() MiniNotify.remove(id) end, level_data.duration)
|
||||
end
|
||||
return function(msg, level)
|
||||
if not vim.in_fast_event() then return notify(msg, level) end
|
||||
vim.schedule(function() notify(msg, level) end)
|
||||
end
|
||||
end
|
||||
|
||||
--- Add notification
|
||||
---
|
||||
--- Add notification to history. It is considered "active" and is shown.
|
||||
--- To hide, call |MiniNotfiy.remove()| with identifier this function returns.
|
||||
---
|
||||
--- Example: >lua
|
||||
---
|
||||
--- local id = MiniNotify.add('Hello', 'WARN', 'Comment')
|
||||
--- vim.defer_fn(function() MiniNotify.remove(id) end, 1000)
|
||||
--- <
|
||||
---@param msg string Notification message.
|
||||
---@param level string|nil Notification level as key of |vim.log.levels|.
|
||||
--- Default: `'INFO'`.
|
||||
---@param hl_group string|nil Notification highlight group.
|
||||
--- Default: `'MiniNotifyNormal'`.
|
||||
---
|
||||
---@return number Notification identifier.
|
||||
MiniNotify.add = function(msg, level, hl_group)
|
||||
H.validate_msg(msg)
|
||||
level = level or 'INFO'
|
||||
H.validate_level(level)
|
||||
hl_group = hl_group or 'MiniNotifyNormal'
|
||||
H.validate_hl_group(hl_group)
|
||||
|
||||
local cur_ts = H.get_timestamp()
|
||||
local new_notif = { msg = msg, level = level, hl_group = hl_group, ts_add = cur_ts, ts_update = cur_ts }
|
||||
|
||||
local new_id = #H.history + 1
|
||||
-- NOTE: Crucial to use the same table here and later only update values
|
||||
-- inside of it in place. This makes sure that history entries are in sync.
|
||||
H.history[new_id], H.active[new_id] = new_notif, new_notif
|
||||
|
||||
-- Refresh active notifications
|
||||
MiniNotify.refresh()
|
||||
|
||||
return new_id
|
||||
end
|
||||
|
||||
--- Update active notification
|
||||
---
|
||||
--- Modify data of active notification.
|
||||
---
|
||||
---@param id number Identifier of currently active notification as returned
|
||||
--- by |MiniNotify.add()|.
|
||||
---@param new_data table Table with data to update. Keys should be as non-timestamp
|
||||
--- fields of |MiniNotify-specification| and values - new notification values.
|
||||
MiniNotify.update = function(id, new_data)
|
||||
local notif = H.active[id]
|
||||
if notif == nil then H.error('`id` is not an identifier of active notification.') end
|
||||
if type(new_data) ~= 'table' then H.error('`new_data` should be table.') end
|
||||
|
||||
if new_data.msg ~= nil then H.validate_msg(new_data.msg) end
|
||||
if new_data.level ~= nil then H.validate_level(new_data.level) end
|
||||
if new_data.hl_group ~= nil then H.validate_hl_group(new_data.hl_group) end
|
||||
|
||||
notif.msg = new_data.msg or notif.msg
|
||||
notif.level = new_data.level or notif.level
|
||||
notif.hl_group = new_data.hl_group or notif.hl_group
|
||||
notif.ts_update = H.get_timestamp()
|
||||
|
||||
MiniNotify.refresh()
|
||||
end
|
||||
|
||||
--- Remove notification
|
||||
---
|
||||
--- If notification is active, make it not active (by setting `ts_remove` field).
|
||||
--- If not active, do nothing.
|
||||
---
|
||||
---@param id number|nil Identifier of previously added notification.
|
||||
--- If it is not, nothing is done (silently).
|
||||
MiniNotify.remove = function(id)
|
||||
local notif = H.active[id]
|
||||
if notif == nil then return end
|
||||
notif.ts_remove = H.get_timestamp()
|
||||
H.active[id] = nil
|
||||
|
||||
MiniNotify.refresh()
|
||||
end
|
||||
|
||||
--- Remove all active notifications
|
||||
---
|
||||
--- Hide all active notifications and stop showing window (if shown).
|
||||
MiniNotify.clear = function()
|
||||
local cur_ts = H.get_timestamp()
|
||||
for id, _ in pairs(H.active) do
|
||||
H.active[id].ts_remove = cur_ts
|
||||
end
|
||||
H.active = {}
|
||||
|
||||
MiniNotify.refresh()
|
||||
end
|
||||
|
||||
--- Refresh notification window
|
||||
---
|
||||
--- Make notification window show relevant data:
|
||||
--- - Create an array of active notifications (see |MiniNotify-specification|).
|
||||
--- - Apply `config.content.sort` to an array. If output has zero notifications,
|
||||
--- make notification window to not show.
|
||||
--- - Apply `config.content.format` to each element of notification array and
|
||||
--- update its message.
|
||||
--- - Construct content from notifications and show them in a window.
|
||||
MiniNotify.refresh = function()
|
||||
if H.is_disabled() or type(vim.v.exiting) == 'number' then return H.window_close() end
|
||||
|
||||
-- Prepare array of active notifications
|
||||
local notif_arr = vim.deepcopy(vim.tbl_values(H.active))
|
||||
local config_content = H.get_config().content
|
||||
|
||||
local sort = vim.is_callable(config_content.sort) and config_content.sort or MiniNotify.default_sort
|
||||
notif_arr = sort(notif_arr)
|
||||
if not H.is_notification_array(notif_arr) then H.error('Output of `content.sort` should be notification array.') end
|
||||
if #notif_arr == 0 then return H.window_close() end
|
||||
|
||||
local format = vim.is_callable(config_content.format) and config_content.format or MiniNotify.default_format
|
||||
notif_arr = H.notif_apply_format(notif_arr, format)
|
||||
|
||||
-- Refresh buffer
|
||||
local buf_id = H.cache.buf_id
|
||||
if not H.is_valid_buf(buf_id) then buf_id = H.buffer_create() end
|
||||
H.buffer_refresh(buf_id, notif_arr)
|
||||
|
||||
-- Refresh window
|
||||
local win_id = H.cache.win_id
|
||||
if not (H.is_valid_win(win_id) and H.is_win_in_tabpage(win_id)) then
|
||||
H.window_close()
|
||||
win_id = H.window_open(buf_id)
|
||||
else
|
||||
local new_config = H.window_compute_config(buf_id)
|
||||
vim.api.nvim_win_set_config(win_id, new_config)
|
||||
end
|
||||
|
||||
-- Redraw
|
||||
vim.cmd('redraw')
|
||||
|
||||
-- Update cache
|
||||
H.cache.buf_id, H.cache.win_id = buf_id, win_id
|
||||
end
|
||||
|
||||
--- Get previously added notification by id
|
||||
---
|
||||
---@param id number Identifier of notification.
|
||||
---
|
||||
---@return table Notification object (see |MiniNotify-specification|).
|
||||
MiniNotify.get = function(id) return vim.deepcopy(H.history[id]) end
|
||||
|
||||
--- Get all previously added notifications
|
||||
---
|
||||
--- Get map of used notifications with keys being notification identifiers.
|
||||
---
|
||||
--- Can be used to get only active notification objects. Example: >lua
|
||||
---
|
||||
--- -- Get active notifications
|
||||
--- vim.tbl_filter(
|
||||
--- function(notif) return notif.ts_remove == nil end,
|
||||
--- MiniNotify.get_all()
|
||||
--- )
|
||||
--- <
|
||||
---@return table Map with notification object values (see |MiniNotify-specification|).
|
||||
--- Note: messages are taken from last valid update.
|
||||
MiniNotify.get_all = function() return vim.deepcopy(H.history) end
|
||||
|
||||
--- Show history
|
||||
---
|
||||
--- Open or reuse a scratch buffer with all previously shown notifications.
|
||||
---
|
||||
--- Notes:
|
||||
--- - Content is ordered from oldest to newest based on latest update time.
|
||||
--- - Message is formatted with `config.content.format`.
|
||||
MiniNotify.show_history = function()
|
||||
-- Prepare content
|
||||
local config_content = H.get_config().content
|
||||
local notif_arr = MiniNotify.get_all()
|
||||
table.sort(notif_arr, function(a, b) return a.ts_update < b.ts_update end)
|
||||
local format = vim.is_callable(config_content.format) and config_content.format or MiniNotify.default_format
|
||||
notif_arr = H.notif_apply_format(notif_arr, format)
|
||||
|
||||
-- Show content in a reusable buffer
|
||||
local buf_id
|
||||
for _, id in ipairs(vim.api.nvim_list_bufs()) do
|
||||
if vim.bo[id].filetype == 'mininotify-history' then buf_id = id end
|
||||
end
|
||||
if buf_id == nil then
|
||||
buf_id = vim.api.nvim_create_buf(true, true)
|
||||
vim.bo[buf_id].filetype = 'mininotify-history'
|
||||
end
|
||||
H.buffer_refresh(buf_id, notif_arr)
|
||||
vim.api.nvim_win_set_buf(0, buf_id)
|
||||
end
|
||||
|
||||
--- Default content format
|
||||
---
|
||||
--- Used by default as `config.content.format`. Prepends notification message
|
||||
--- with the human readable update time and a separator.
|
||||
---
|
||||
---@param notif table Notification object (see |MiniNotify-specification|).
|
||||
---
|
||||
---@return string Formatted notification message.
|
||||
MiniNotify.default_format = function(notif)
|
||||
local time = vim.fn.strftime('%H:%M:%S', math.floor(notif.ts_update))
|
||||
return string.format('%s │ %s', time, notif.msg)
|
||||
end
|
||||
|
||||
--- Default content sort
|
||||
---
|
||||
--- Used by default as `config.content.sort`. First sorts by notification's `level`
|
||||
--- ("ERROR" > "WARN" > "INFO" > "DEBUG" > "TRACE" > "OFF"; the bigger the more
|
||||
--- important); if draw - by latest update time (the later the more important).
|
||||
---
|
||||
---@param notif_arr table Array of notifications (see |MiniNotify-specification|).
|
||||
---
|
||||
---@return table Sorted array of notifications.
|
||||
MiniNotify.default_sort = function(notif_arr)
|
||||
local res = vim.deepcopy(notif_arr)
|
||||
table.sort(res, H.notif_compare)
|
||||
return res
|
||||
end
|
||||
|
||||
-- Helper data ================================================================
|
||||
-- Module default config
|
||||
H.default_config = MiniNotify.config
|
||||
|
||||
-- Map of currently active notifications with their id as key
|
||||
H.active = {}
|
||||
|
||||
-- History of all notifications in order they are created
|
||||
H.history = {}
|
||||
|
||||
-- Map of LSP progress process id to notification data
|
||||
H.lsp_progress = {}
|
||||
|
||||
-- Priorities of levels
|
||||
H.level_priority = { ERROR = 6, WARN = 5, INFO = 4, DEBUG = 3, TRACE = 2, OFF = 1 }
|
||||
|
||||
-- Namespaces
|
||||
H.ns_id = {
|
||||
highlight = vim.api.nvim_create_namespace('MiniNotifyHighlight'),
|
||||
}
|
||||
|
||||
-- Various cache
|
||||
H.cache = {
|
||||
-- Notification buffer and window
|
||||
buf_id = nil,
|
||||
win_id = nil,
|
||||
}
|
||||
|
||||
-- Helper functionality =======================================================
|
||||
-- Settings -------------------------------------------------------------------
|
||||
H.setup_config = function(config)
|
||||
-- General idea: if some table elements are not present in user-supplied
|
||||
-- `config`, take them from default config
|
||||
vim.validate({ config = { config, 'table', true } })
|
||||
config = vim.tbl_deep_extend('force', vim.deepcopy(H.default_config), config or {})
|
||||
|
||||
vim.validate({
|
||||
content = { config.content, 'table' },
|
||||
lsp_progress = { config.lsp_progress, 'table' },
|
||||
window = { config.window, 'table' },
|
||||
})
|
||||
|
||||
local is_table_or_callable = function(x) return type(x) == 'table' or vim.is_callable(x) end
|
||||
vim.validate({
|
||||
['content.format'] = { config.content.format, 'function', true },
|
||||
['content.sort'] = { config.content.sort, 'function', true },
|
||||
['lsp_progress.enable'] = { config.lsp_progress.enable, 'boolean' },
|
||||
['lsp_progress.duration_last'] = { config.lsp_progress.duration_last, 'number' },
|
||||
['window.config'] = { config.window.config, is_table_or_callable, 'table or callable' },
|
||||
['window.max_width_share'] = { config.window.max_width_share, 'number' },
|
||||
['window.winblend'] = { config.window.winblend, 'number' },
|
||||
})
|
||||
|
||||
return config
|
||||
end
|
||||
|
||||
H.apply_config = function(config)
|
||||
MiniNotify.config = config
|
||||
|
||||
if config.lsp_progress.enable then
|
||||
-- Use `vim.schedule` to reduce startup time (sourcing `vim.lsp` is costly)
|
||||
vim.schedule(function()
|
||||
-- Cache original handler only once (to avoid infinite loop)
|
||||
if vim.lsp.handlers['$/progress before mini.notify'] == nil then
|
||||
vim.lsp.handlers['$/progress before mini.notify'] = vim.lsp.handlers['$/progress']
|
||||
end
|
||||
|
||||
vim.lsp.handlers['$/progress'] = H.lsp_progress_handler
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
H.create_autocommands = function(config)
|
||||
local augroup = vim.api.nvim_create_augroup('MiniNotify', {})
|
||||
|
||||
local au = function(event, pattern, callback, desc)
|
||||
vim.api.nvim_create_autocmd(event, { group = augroup, pattern = pattern, callback = callback, desc = desc })
|
||||
end
|
||||
|
||||
au({ 'TabEnter', 'VimResized' }, '*', function() MiniNotify.refresh() end, 'Refresh notifications')
|
||||
end
|
||||
|
||||
--stylua: ignore
|
||||
H.create_default_hl = function()
|
||||
local hi = function(name, opts)
|
||||
opts.default = true
|
||||
vim.api.nvim_set_hl(0, name, opts)
|
||||
end
|
||||
|
||||
hi('MiniNotifyBorder', { link = 'FloatBorder' })
|
||||
hi('MiniNotifyNormal', { link = 'NormalFloat' })
|
||||
hi('MiniNotifyTitle', { link = 'FloatTitle' })
|
||||
end
|
||||
|
||||
H.is_disabled = function() return vim.g.mininotify_disable == true or vim.b.mininotify_disable == true end
|
||||
|
||||
H.get_config = function(config)
|
||||
return vim.tbl_deep_extend('force', MiniNotify.config, vim.b.mininotify_config or {}, config or {})
|
||||
end
|
||||
|
||||
-- LSP progress ---------------------------------------------------------------
|
||||
H.lsp_progress_handler = function(err, result, ctx, config)
|
||||
-- Make basic response processing. First call original LSP handler.
|
||||
-- On Neovim>=0.10 this is crucial to not override `LspProgress` event.
|
||||
if vim.is_callable(vim.lsp.handlers['$/progress before mini.notify']) then
|
||||
vim.lsp.handlers['$/progress before mini.notify'](err, result, ctx, config)
|
||||
end
|
||||
|
||||
local lsp_progress_config = H.get_config().lsp_progress
|
||||
if not lsp_progress_config.enable then return end
|
||||
|
||||
if err ~= nil then return vim.notify(vim.inspect(err), vim.log.levels.ERROR) end
|
||||
if not (type(result) == 'table' and type(result.value) == 'table') then return end
|
||||
local value = result.value
|
||||
|
||||
-- Construct LSP progress id
|
||||
local client_name = vim.lsp.get_client_by_id(ctx.client_id).name
|
||||
if type(client_name) ~= 'string' then client_name = string.format('LSP[id=%s]', ctx.client_id) end
|
||||
|
||||
local buf_id = ctx.bufnr or 'nil'
|
||||
local lsp_progress_id = buf_id .. client_name .. (result.token or '')
|
||||
local progress_data = H.lsp_progress[lsp_progress_id] or {}
|
||||
|
||||
-- Store percentage to be used if no new one was sent
|
||||
progress_data.percentage = value.percentage or progress_data.percentage or 0
|
||||
|
||||
-- Stop notifications without update on progress end.
|
||||
-- This usually results into a cleaner and more informative history.
|
||||
-- Delay removal to not cause flicker.
|
||||
if value.kind == 'end' then
|
||||
H.lsp_progress[lsp_progress_id] = nil
|
||||
local delay = math.max(lsp_progress_config.duration_last, 0)
|
||||
vim.defer_fn(function() MiniNotify.remove(progress_data.notif_id) end, delay)
|
||||
return
|
||||
end
|
||||
|
||||
-- Cache title because it is only supplied on 'begin'
|
||||
if value.kind == 'begin' then progress_data.title = value.title end
|
||||
|
||||
-- Make notification
|
||||
--stylua: ignore
|
||||
local msg = string.format(
|
||||
'%s: %s %s (%s%%)',
|
||||
client_name, progress_data.title or '', value.message or '', progress_data.percentage
|
||||
)
|
||||
|
||||
if progress_data.notif_id == nil then
|
||||
progress_data.notif_id = MiniNotify.add(msg)
|
||||
else
|
||||
MiniNotify.update(progress_data.notif_id, { msg = msg })
|
||||
end
|
||||
|
||||
-- Cache progress data
|
||||
H.lsp_progress[lsp_progress_id] = progress_data
|
||||
end
|
||||
|
||||
-- Buffer ---------------------------------------------------------------------
|
||||
H.buffer_create = function()
|
||||
local buf_id = vim.api.nvim_create_buf(false, true)
|
||||
vim.bo[buf_id].filetype = 'mininotify'
|
||||
return buf_id
|
||||
end
|
||||
|
||||
H.buffer_refresh = function(buf_id, notif_arr)
|
||||
local ns_id = H.ns_id.highlight
|
||||
|
||||
-- Ensure clear buffer
|
||||
vim.api.nvim_buf_clear_namespace(buf_id, ns_id, 0, -1)
|
||||
vim.api.nvim_buf_set_lines(buf_id, 0, -1, true, {})
|
||||
|
||||
-- Compute lines and highlight regions
|
||||
local lines, highlights = {}, {}
|
||||
for _, notif in ipairs(notif_arr) do
|
||||
local notif_lines = vim.split(notif.msg, '\n')
|
||||
for _, l in ipairs(notif_lines) do
|
||||
table.insert(lines, l)
|
||||
end
|
||||
table.insert(highlights, { group = notif.hl_group, from_line = #lines - #notif_lines + 1, to_line = #lines })
|
||||
end
|
||||
|
||||
-- Set lines and highlighting
|
||||
vim.api.nvim_buf_set_lines(buf_id, 0, -1, true, lines)
|
||||
local extmark_opts = { end_col = 0, hl_eol = true, hl_mode = 'combine' }
|
||||
for _, hi_data in ipairs(highlights) do
|
||||
extmark_opts.end_row, extmark_opts.hl_group = hi_data.to_line, hi_data.group
|
||||
vim.api.nvim_buf_set_extmark(buf_id, ns_id, hi_data.from_line - 1, 0, extmark_opts)
|
||||
end
|
||||
end
|
||||
|
||||
H.buffer_default_dimensions = function(buf_id, max_width_share)
|
||||
local line_widths = vim.tbl_map(vim.fn.strdisplaywidth, vim.api.nvim_buf_get_lines(buf_id, 0, -1, true))
|
||||
|
||||
-- Compute width so as to fit all lines
|
||||
local width = 1
|
||||
for _, l_w in ipairs(line_widths) do
|
||||
width = math.max(width, l_w)
|
||||
end
|
||||
-- - Limit from above for better visuals
|
||||
max_width_share = math.min(math.max(max_width_share, 0), 1)
|
||||
local max_width = math.max(math.floor(max_width_share * vim.o.columns), 1)
|
||||
width = math.min(width, max_width)
|
||||
|
||||
-- Compute height based on the width so as to fit all lines with 'wrap' on
|
||||
local height = 0
|
||||
for _, l_w in ipairs(line_widths) do
|
||||
height = height + math.floor(math.max(l_w - 1, 0) / width) + 1
|
||||
end
|
||||
|
||||
return width, height
|
||||
end
|
||||
|
||||
-- Window ---------------------------------------------------------------------
|
||||
H.window_open = function(buf_id)
|
||||
local config = H.window_compute_config(buf_id, true)
|
||||
local win_id = vim.api.nvim_open_win(buf_id, false, config)
|
||||
|
||||
vim.wo[win_id].foldenable = false
|
||||
vim.wo[win_id].wrap = true
|
||||
vim.wo[win_id].winblend = H.get_config().window.winblend
|
||||
vim.wo[win_id].winhighlight = 'NormalFloat:MiniNotifyNormal,FloatBorder:MiniNotifyBorder,FloatTitle:MiniNotifyTitle'
|
||||
|
||||
return win_id
|
||||
end
|
||||
|
||||
H.window_compute_config = function(buf_id, is_for_open)
|
||||
local has_tabline = vim.o.showtabline == 2 or (vim.o.showtabline == 1 and #vim.api.nvim_list_tabpages() > 1)
|
||||
local has_statusline = vim.o.laststatus > 0
|
||||
local max_height = vim.o.lines - vim.o.cmdheight - (has_tabline and 1 or 0) - (has_statusline and 1 or 0)
|
||||
local max_width = vim.o.columns
|
||||
|
||||
local config_win = H.get_config().window
|
||||
local default_config = { relative = 'editor', style = 'minimal', noautocmd = is_for_open, zindex = 999 }
|
||||
default_config.anchor, default_config.col, default_config.row = 'NE', vim.o.columns, has_tabline and 1 or 0
|
||||
default_config.width, default_config.height = H.buffer_default_dimensions(buf_id, config_win.max_width_share)
|
||||
default_config.border = 'single'
|
||||
-- Don't allow focus to not disrupt window navigation
|
||||
default_config.focusable = false
|
||||
|
||||
local win_config = config_win.config
|
||||
if vim.is_callable(win_config) then win_config = win_config(buf_id) end
|
||||
local config = vim.tbl_deep_extend('force', default_config, win_config or {})
|
||||
|
||||
-- Tweak config values to ensure they are proper, accounting for border
|
||||
local offset = config.border == 'none' and 0 or 2
|
||||
config.height = math.min(config.height, max_height - offset)
|
||||
config.width = math.min(config.width, max_width - offset)
|
||||
|
||||
return config
|
||||
end
|
||||
|
||||
H.window_close = function()
|
||||
if H.is_valid_win(H.cache.win_id) then vim.api.nvim_win_close(H.cache.win_id, true) end
|
||||
H.cache.win_id = nil
|
||||
end
|
||||
|
||||
-- Notifications --------------------------------------------------------------
|
||||
H.validate_msg = function(x)
|
||||
if type(x) ~= 'string' then H.error('`msg` should be string.') end
|
||||
end
|
||||
|
||||
H.validate_level = function(x)
|
||||
if vim.log.levels[x] == nil then H.error('`level` should be key of `vim.log.levels`.') end
|
||||
end
|
||||
|
||||
H.validate_hl_group = function(x)
|
||||
if type(x) ~= 'string' then H.error('`hl_group` should be string.') end
|
||||
end
|
||||
|
||||
H.is_notification = function(x)
|
||||
return type(x) == 'table'
|
||||
and type(x.msg) == 'string'
|
||||
and vim.log.levels[x.level] ~= nil
|
||||
and type(x.hl_group) == 'string'
|
||||
and type(x.ts_add) == 'number'
|
||||
and type(x.ts_update) == 'number'
|
||||
and (x.ts_remove == nil or type(x.ts_remove) == 'number')
|
||||
end
|
||||
|
||||
H.is_notification_array = function(x)
|
||||
if not H.islist(x) then return false end
|
||||
for _, y in ipairs(x) do
|
||||
if not H.is_notification(y) then return false end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
H.notif_apply_format = function(notif_arr, format)
|
||||
for _, notif in ipairs(notif_arr) do
|
||||
local res = format(notif)
|
||||
if type(res) ~= 'string' then H.error('Output of `content.format` should be string.') end
|
||||
notif.msg = res
|
||||
end
|
||||
return notif_arr
|
||||
end
|
||||
|
||||
H.notif_compare = function(a, b)
|
||||
local a_priority, b_priority = H.level_priority[a.level], H.level_priority[b.level]
|
||||
return a_priority > b_priority or (a_priority == b_priority and a.ts_update > b.ts_update)
|
||||
end
|
||||
|
||||
-- Utilities ------------------------------------------------------------------
|
||||
H.error = function(msg) error(string.format('(mini.notify) %s', msg), 0) end
|
||||
|
||||
H.is_valid_buf = function(buf_id) return type(buf_id) == 'number' and vim.api.nvim_buf_is_valid(buf_id) end
|
||||
|
||||
H.is_valid_win = function(win_id) return type(win_id) == 'number' and vim.api.nvim_win_is_valid(win_id) end
|
||||
|
||||
H.is_win_in_tabpage = function(win_id) return vim.api.nvim_win_get_tabpage(win_id) == vim.api.nvim_get_current_tabpage() end
|
||||
|
||||
H.get_timestamp = function()
|
||||
-- This is more acceptable for `vim.fn.strftime()` than `vim.loop.hrtime()`
|
||||
local seconds, microseconds = vim.loop.gettimeofday()
|
||||
return seconds + 0.000001 * microseconds
|
||||
end
|
||||
|
||||
-- TODO: Remove after compatibility with Neovim=0.9 is dropped
|
||||
H.islist = vim.fn.has('nvim-0.10') == 1 and vim.islist or vim.tbl_islist
|
||||
|
||||
return MiniNotify
|
||||
1278
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/operators.lua
Normal file
1278
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/operators.lua
Normal file
File diff suppressed because it is too large
Load Diff
645
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/pairs.lua
Normal file
645
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/pairs.lua
Normal file
@ -0,0 +1,645 @@
|
||||
--- *mini.pairs* Autopairs
|
||||
--- *MiniPairs*
|
||||
---
|
||||
--- MIT License Copyright (c) 2021 Evgeni Chasnovski
|
||||
---
|
||||
--- ==============================================================================
|
||||
---
|
||||
--- Features:
|
||||
--- - Functionality to work with 'paired' characters conditional on cursor's
|
||||
--- neighborhood (two characters to its left and right).
|
||||
---
|
||||
--- - Usage should be through making appropriate mappings using |MiniPairs.map|
|
||||
--- or in |MiniPairs.setup| (for global mapping), |MiniPairs.map_buf| (for
|
||||
--- buffer mapping).
|
||||
---
|
||||
--- - Pairs get automatically registered to be recognized by `<BS>` and `<CR>`.
|
||||
---
|
||||
--- What it doesn't do:
|
||||
--- - It doesn't support multiple characters as "open" and "close" symbols. Use
|
||||
--- snippets for that.
|
||||
---
|
||||
--- - It doesn't support dependency on filetype. Use |i_CTRL-V| to insert
|
||||
--- single symbol or `autocmd` command or 'after/ftplugin' approach to:
|
||||
--- - `:lua MiniPairs.map_buf(0, 'i', <*>, <pair_info>)` - make new mapping
|
||||
--- for '<*>' in current buffer.
|
||||
--- - `:lua MiniPairs.unmap_buf(0, 'i', <*>, <pair>)` - unmap key `<*>` while
|
||||
--- unregistering `<pair>` pair in current buffer. Note: this reverts
|
||||
--- mapping done by |MiniPairs.map_buf|. If mapping was done with
|
||||
--- |MiniPairs.map|, unmap for buffer in usual Neovim manner:
|
||||
--- `inoremap <buffer> <*> <*>` (this maps `<*>` key to do the same it
|
||||
--- does by default).
|
||||
--- - Disable module for buffer (see 'Disabling' section).
|
||||
---
|
||||
--- # Setup ~
|
||||
---
|
||||
--- This module needs a setup with `require('mini.pairs').setup({})`
|
||||
--- (replace `{}` with your `config` table). It will create global Lua table
|
||||
--- `MiniPairs` which you can use for scripting or manually (with
|
||||
--- `:lua MiniPairs.*`).
|
||||
---
|
||||
--- See |MiniPairs.config| for `config` structure and default values.
|
||||
---
|
||||
--- This module doesn't have runtime options, so using `vim.b.minipairs_config`
|
||||
--- will have no effect here.
|
||||
---
|
||||
--- # Example mappings ~
|
||||
--- >lua
|
||||
--- -- Register quotes inside `config` of `MiniPairs.setup()`
|
||||
--- mappings = {
|
||||
--- ['"'] = { register = { cr = true } },
|
||||
--- ["'"] = { register = { cr = true } },
|
||||
--- }
|
||||
---
|
||||
--- -- Insert `<>` pair if `<` is typed at line start, don't register for `<CR>`
|
||||
--- local lt_opts = {
|
||||
--- action = 'open',
|
||||
--- pair = '<>',
|
||||
--- neigh_pattern = '\r.',
|
||||
--- register = { cr = false },
|
||||
--- }
|
||||
--- MiniPairs.map('i', '<', lt_opts)
|
||||
---
|
||||
--- local gt_opts = { action = 'close', pair = '<>', register = { cr = false } }
|
||||
--- MiniPairs.map('i', '>', gt_opts)
|
||||
---
|
||||
--- -- Create symmetrical `$$` pair only in Tex files
|
||||
--- local map_tex = function()
|
||||
--- MiniPairs.map_buf(0, 'i', '$', { action = 'closeopen', pair = '$$' })
|
||||
--- end
|
||||
--- vim.api.nvim_create_autocmd(
|
||||
--- 'FileType',
|
||||
--- { pattern = 'tex', callback = map_tex }
|
||||
--- )
|
||||
--- <
|
||||
--- # Notes ~
|
||||
---
|
||||
--- - Make sure to make proper mapping of `<CR>` in order to support completion
|
||||
--- plugin of your choice:
|
||||
--- - For |MiniCompletion| see 'Helpful key mappings' section.
|
||||
--- - For current implementation of "hrsh7th/nvim-cmp" there is no need to
|
||||
--- make custom mapping. You can use default setup, which will confirm
|
||||
--- completion selection if popup is visible and expand pair otherwise.
|
||||
--- - Having mapping in terminal mode can conflict with:
|
||||
--- - Autopairing capabilities of interpretators (`ipython`, `radian`).
|
||||
--- - Vim mode of terminal itself.
|
||||
---
|
||||
--- # Disabling ~
|
||||
---
|
||||
--- To disable, set `vim.g.minipairs_disable` (globally) or `vim.b.minipairs_disable`
|
||||
--- (for a buffer) to `true`. Considering high number of different scenarios
|
||||
--- and customization intentions, writing exact rules for disabling module's
|
||||
--- functionality is left to user. See |mini.nvim-disabling-recipes| for common
|
||||
--- recipes.
|
||||
|
||||
---@alias __pairs_neigh_pattern string|nil Pattern for two neighborhood characters.
|
||||
--- Character "\r" indicates line start, "\n" - line end.
|
||||
---@alias __pairs_pair string String with two characters representing pair.
|
||||
---@alias __pairs_unregistered_pair string Pair which should be unregistered from both
|
||||
--- `<BS>` and `<CR>`. Should be explicitly supplied to avoid confusion.
|
||||
--- Supply `''` to not unregister pair.
|
||||
|
||||
-- Module definition ==========================================================
|
||||
local MiniPairs = {}
|
||||
local H = {}
|
||||
|
||||
--- Module setup
|
||||
---
|
||||
---@param config table|nil Module config table. See |MiniPairs.config|.
|
||||
---
|
||||
---@usage >lua
|
||||
--- require('mini.pairs').setup() -- use default config
|
||||
--- -- OR
|
||||
--- require('mini.pairs').setup({}) -- replace {} with your config table
|
||||
--- <
|
||||
MiniPairs.setup = function(config)
|
||||
-- Export module
|
||||
_G.MiniPairs = MiniPairs
|
||||
|
||||
-- Setup config
|
||||
config = H.setup_config(config)
|
||||
|
||||
-- Apply config
|
||||
H.apply_config(config)
|
||||
|
||||
-- Define behavior
|
||||
H.create_autocommands()
|
||||
end
|
||||
|
||||
--stylua: ignore
|
||||
--- Module config
|
||||
---
|
||||
--- Default values:
|
||||
---@eval return MiniDoc.afterlines_to_code(MiniDoc.current.eval_section)
|
||||
MiniPairs.config = {
|
||||
-- In which modes mappings from this `config` should be created
|
||||
modes = { insert = true, command = false, terminal = false },
|
||||
|
||||
-- Global mappings. Each right hand side should be a pair information, a
|
||||
-- table with at least these fields (see more in |MiniPairs.map|):
|
||||
-- - <action> - one of "open", "close", "closeopen".
|
||||
-- - <pair> - two character string for pair to be used.
|
||||
-- By default pair is not inserted after `\`, quotes are not recognized by
|
||||
-- `<CR>`, `'` does not insert pair after a letter.
|
||||
-- Only parts of tables can be tweaked (others will use these defaults).
|
||||
-- Supply `false` instead of table to not map particular key.
|
||||
mappings = {
|
||||
['('] = { action = 'open', pair = '()', neigh_pattern = '[^\\].' },
|
||||
['['] = { action = 'open', pair = '[]', neigh_pattern = '[^\\].' },
|
||||
['{'] = { action = 'open', pair = '{}', neigh_pattern = '[^\\].' },
|
||||
|
||||
[')'] = { action = 'close', pair = '()', neigh_pattern = '[^\\].' },
|
||||
[']'] = { action = 'close', pair = '[]', neigh_pattern = '[^\\].' },
|
||||
['}'] = { action = 'close', pair = '{}', neigh_pattern = '[^\\].' },
|
||||
|
||||
['"'] = { action = 'closeopen', pair = '""', neigh_pattern = '[^\\].', register = { cr = false } },
|
||||
["'"] = { action = 'closeopen', pair = "''", neigh_pattern = '[^%a\\].', register = { cr = false } },
|
||||
['`'] = { action = 'closeopen', pair = '``', neigh_pattern = '[^\\].', register = { cr = false } },
|
||||
},
|
||||
}
|
||||
--minidoc_afterlines_end
|
||||
|
||||
-- Module functionality =======================================================
|
||||
--- Make global mapping
|
||||
---
|
||||
--- This is a wrapper for |nvim_set_keymap()| but instead of right hand side of
|
||||
--- mapping (as string) it expects table with pair information.
|
||||
---
|
||||
--- Using this function instead of |nvim_set_keymap()| allows automatic
|
||||
--- registration of pairs which will be recognized by `<BS>` and `<CR>`.
|
||||
--- It also infers mapping description from `pair_info`.
|
||||
---
|
||||
---@param mode string `mode` for |nvim_set_keymap()|.
|
||||
---@param lhs string `lhs` for |nvim_set_keymap()|.
|
||||
---@param pair_info table Table with pair information. Fields:
|
||||
--- - <action> - one of "open" (for |MiniPairs.open|),
|
||||
--- "close" (for |MiniPairs.close|), or "closeopen" (for |MiniPairs.closeopen|).
|
||||
--- - <pair> - two character string to be used as argument for action function.
|
||||
--- - <neigh_pattern> - optional 'two character' neighborhood pattern to be
|
||||
--- used as argument for action function.
|
||||
--- Default: `'..'` (no restriction from neighborhood).
|
||||
--- - <register> - optional table with information about whether this pair will
|
||||
--- be recognized by `<BS>` (in |MiniPairs.bs|) and/or `<CR>` (in |MiniPairs.cr|).
|
||||
--- Should have boolean fields <bs> and <cr> which are both `true` by
|
||||
--- default (if not overridden explicitly).
|
||||
---@param opts table|nil Optional table `opts` for |nvim_set_keymap()|. Elements
|
||||
--- `expr` and `noremap` won't be recognized (`true` by default).
|
||||
MiniPairs.map = function(mode, lhs, pair_info, opts)
|
||||
pair_info = H.validate_pair_info(pair_info)
|
||||
opts = vim.tbl_deep_extend('force', opts or {}, { expr = true, noremap = true })
|
||||
opts.desc = H.infer_mapping_description(pair_info)
|
||||
|
||||
vim.api.nvim_set_keymap(mode, lhs, H.pair_info_to_map_rhs(pair_info), opts)
|
||||
H.register_pair(pair_info, mode, 'all')
|
||||
|
||||
-- Ensure that `<BS>` and `<CR>` are mapped for input mode
|
||||
H.ensure_cr_bs(mode)
|
||||
end
|
||||
|
||||
--- Make buffer mapping
|
||||
---
|
||||
--- This is a wrapper for |nvim_buf_set_keymap()| but instead of string right
|
||||
--- hand side of mapping it expects table with pair information similar to one
|
||||
--- in |MiniPairs.map|.
|
||||
---
|
||||
--- Using this function instead of |nvim_buf_set_keymap()| allows automatic
|
||||
--- registration of pairs which will be recognized by `<BS>` and `<CR>`.
|
||||
--- It also infers mapping description from `pair_info`.
|
||||
---
|
||||
---@param buffer number `buffer` for |nvim_buf_set_keymap()|.
|
||||
---@param mode string `mode` for |nvim_buf_set_keymap()|.
|
||||
---@param lhs string `lhs` for |nvim_buf_set_keymap()|.
|
||||
---@param pair_info table Table with pair information.
|
||||
---@param opts table|nil Optional table `opts` for |nvim_buf_set_keymap()|.
|
||||
--- Elements `expr` and `noremap` won't be recognized (`true` by default).
|
||||
MiniPairs.map_buf = function(buffer, mode, lhs, pair_info, opts)
|
||||
pair_info = H.validate_pair_info(pair_info)
|
||||
opts = vim.tbl_deep_extend('force', opts or {}, { expr = true, noremap = true })
|
||||
opts.desc = H.infer_mapping_description(pair_info)
|
||||
|
||||
vim.api.nvim_buf_set_keymap(buffer, mode, lhs, H.pair_info_to_map_rhs(pair_info), opts)
|
||||
H.register_pair(pair_info, mode, buffer == 0 and vim.api.nvim_get_current_buf() or buffer)
|
||||
|
||||
-- Ensure that `<BS>` and `<CR>` are mapped for input mode
|
||||
H.ensure_cr_bs(mode)
|
||||
end
|
||||
|
||||
--- Remove global mapping
|
||||
---
|
||||
--- A wrapper for |nvim_del_keymap()| which registers supplied `pair`.
|
||||
---
|
||||
---@param mode string `mode` for |nvim_del_keymap()|.
|
||||
---@param lhs string `lhs` for |nvim_del_keymap()|.
|
||||
---@param pair __pairs_unregistered_pair
|
||||
MiniPairs.unmap = function(mode, lhs, pair)
|
||||
-- `pair` should be supplied explicitly
|
||||
vim.validate({ pair = { pair, 'string' } })
|
||||
|
||||
-- Use `pcall` to allow 'deleting' already deleted mapping
|
||||
pcall(vim.api.nvim_del_keymap, mode, lhs)
|
||||
if pair == '' then return end
|
||||
H.unregister_pair(pair, mode, 'all')
|
||||
end
|
||||
|
||||
--- Remove buffer mapping
|
||||
---
|
||||
--- Wrapper for |nvim_buf_del_keymap()| which also unregisters supplied `pair`.
|
||||
---
|
||||
--- Note: this only reverts mapping done by |MiniPairs.map_buf|. If mapping was
|
||||
--- done with |MiniPairs.map|, revert to default behavior for buffer: >lua
|
||||
---
|
||||
--- -- Map `X` key to do the same it does by default
|
||||
--- vim.keymap.set('i', 'X', 'X', { buffer = true })
|
||||
--- <
|
||||
---@param buffer number `buffer` for |nvim_buf_del_keymap()|.
|
||||
---@param mode string `mode` for |nvim_buf_del_keymap()|.
|
||||
---@param lhs string `lhs` for |nvim_buf_del_keymap()|.
|
||||
---@param pair __pairs_unregistered_pair
|
||||
MiniPairs.unmap_buf = function(buffer, mode, lhs, pair)
|
||||
-- `pair` should be supplied explicitly
|
||||
vim.validate({ pair = { pair, 'string' } })
|
||||
|
||||
-- Use `pcall` to allow 'deleting' already deleted mapping
|
||||
pcall(vim.api.nvim_buf_del_keymap, buffer, mode, lhs)
|
||||
if pair == '' then return end
|
||||
H.unregister_pair(pair, mode, buffer == 0 and vim.api.nvim_get_current_buf() or buffer)
|
||||
end
|
||||
|
||||
--- Process "open" symbols
|
||||
---
|
||||
--- Used as |map-expr| mapping for "open" symbols in asymmetric pair ('(', '[',
|
||||
--- etc.). If neighborhood doesn't match supplied pattern, function results
|
||||
--- into "open" symbol. Otherwise, it pastes whole pair and moves inside pair
|
||||
--- with |<Left>|.
|
||||
---
|
||||
--- Used inside |MiniPairs.map| and |MiniPairs.map_buf| for an actual mapping.
|
||||
---
|
||||
---@param pair __pairs_pair
|
||||
---@param neigh_pattern __pairs_neigh_pattern
|
||||
---
|
||||
---@return string Keys performing "open" action.
|
||||
MiniPairs.open = function(pair, neigh_pattern)
|
||||
if H.is_disabled() or not H.neigh_match(neigh_pattern) then return pair:sub(1, 1) end
|
||||
|
||||
return ('%s%s'):format(pair, H.get_arrow_key('left'))
|
||||
end
|
||||
|
||||
--- Process "close" symbols
|
||||
---
|
||||
--- Used as |map-expr| mapping for "close" symbols in asymmetric pair (')',
|
||||
--- ']', etc.). If neighborhood doesn't match supplied pattern, function
|
||||
--- results into "close" symbol. Otherwise it jumps over symbol to the right of
|
||||
--- cursor (with |<Right>|) if it is equal to "close" one and inserts it
|
||||
--- otherwise.
|
||||
---
|
||||
--- Used inside |MiniPairs.map| and |MiniPairs.map_buf| for an actual mapping.
|
||||
---
|
||||
---@param pair __pairs_pair
|
||||
---@param neigh_pattern __pairs_neigh_pattern
|
||||
---
|
||||
---@return string Keys performing "close" action.
|
||||
MiniPairs.close = function(pair, neigh_pattern)
|
||||
if H.is_disabled() or not H.neigh_match(neigh_pattern) then return pair:sub(2, 2) end
|
||||
|
||||
local close = pair:sub(2, 2)
|
||||
if H.get_cursor_neigh(1, 1) == close then
|
||||
return H.get_arrow_key('right')
|
||||
else
|
||||
return close
|
||||
end
|
||||
end
|
||||
|
||||
--- Process "closeopen" symbols
|
||||
---
|
||||
--- Used as |map-expr| mapping for 'symmetrical' symbols (from pairs '""',
|
||||
--- '\'\'', '``'). It tries to perform 'closeopen action': move over right
|
||||
--- character (with |<Right>|) if it is equal to second character from pair or
|
||||
--- conditionally paste pair otherwise (with |MiniPairs.open()|).
|
||||
---
|
||||
--- Used inside |MiniPairs.map| and |MiniPairs.map_buf| for an actual mapping.
|
||||
---
|
||||
---@param pair __pairs_pair
|
||||
---@param neigh_pattern __pairs_neigh_pattern
|
||||
---
|
||||
---@return string Keys performing "closeopen" action.
|
||||
MiniPairs.closeopen = function(pair, neigh_pattern)
|
||||
if H.is_disabled() or H.get_cursor_neigh(1, 1) ~= pair:sub(2, 2) then
|
||||
return MiniPairs.open(pair, neigh_pattern)
|
||||
else
|
||||
return H.get_arrow_key('right')
|
||||
end
|
||||
end
|
||||
|
||||
--- Process |<BS>|
|
||||
---
|
||||
--- Used as |map-expr| mapping for `<BS>` in Insert mode. It removes whole pair
|
||||
--- (via executing `<Del>` after input key) if neighborhood is equal to a whole
|
||||
--- pair recognized for current buffer. Pair is recognized for current buffer
|
||||
--- if it is registered for global or current buffer mapping. Pair is
|
||||
--- registered as a result of calling |MiniPairs.map| or |MiniPairs.map_buf|.
|
||||
---
|
||||
--- Mapped by default inside |MiniPairs.setup|.
|
||||
---
|
||||
--- This can be used to modify other Insert mode keys to respect neighborhood
|
||||
--- pair. Examples: >lua
|
||||
---
|
||||
--- local map_bs = function(lhs, rhs)
|
||||
--- vim.keymap.set('i', lhs, rhs, { expr = true, replace_keycodes = false })
|
||||
--- end
|
||||
---
|
||||
--- map_bs('<C-h>', 'v:lua.MiniPairs.bs()')
|
||||
--- map_bs('<C-w>', 'v:lua.MiniPairs.bs("\23")')
|
||||
--- map_bs('<C-u>', 'v:lua.MiniPairs.bs("\21")')
|
||||
--- <
|
||||
---@param key string|nil Key to use. Default: `<BS>`.
|
||||
---
|
||||
---@return string Keys performing "backspace" action.
|
||||
MiniPairs.bs = function(key)
|
||||
local res = key or H.keys.bs
|
||||
|
||||
local neigh = H.get_cursor_neigh(0, 1)
|
||||
if not H.is_disabled() and H.is_pair_registered(neigh, vim.fn.mode(), 0, 'bs') then
|
||||
res = ('%s%s'):format(res, H.keys.del)
|
||||
end
|
||||
|
||||
return res
|
||||
end
|
||||
|
||||
--- Process |i_<CR>|
|
||||
---
|
||||
--- Used as |map-expr| mapping for `<CR>` in insert mode. It puts "close"
|
||||
--- symbol on next line (via `<CR><C-o>O`) if neighborhood is equal to a whole
|
||||
--- pair recognized for current buffer. Pair is recognized for current buffer
|
||||
--- if it is registered for global or current buffer mapping. Pair is
|
||||
--- registered as a result of calling |MiniPairs.map| or |MiniPairs.map_buf|.
|
||||
---
|
||||
--- Note: some relevant mode changing events are temporarily ignored
|
||||
--- (with |eventignore|) to counter effect of using |i_CTRL-O|.
|
||||
---
|
||||
--- Mapped by default inside |MiniPairs.setup|.
|
||||
---
|
||||
---@param key string|nil Key to use. Default: `<CR>`.
|
||||
---
|
||||
---@return string Keys performing "new line" action.
|
||||
MiniPairs.cr = function(key)
|
||||
local res = key or H.keys.cr
|
||||
|
||||
local neigh = H.get_cursor_neigh(0, 1)
|
||||
if not H.is_disabled() and H.is_pair_registered(neigh, vim.fn.mode(), 0, 'cr') then
|
||||
-- Temporarily ignore mode change to not trigger some common expensive
|
||||
-- autocommands (like diagnostic check, etc.)
|
||||
local cache_eventignore = vim.o.eventignore
|
||||
vim.o.eventignore = 'InsertLeave,InsertLeavePre,InsertEnter,ModeChanged'
|
||||
H.restore_eventignore(cache_eventignore)
|
||||
|
||||
res = ('%s%s'):format(res, H.keys.above)
|
||||
end
|
||||
|
||||
return res
|
||||
end
|
||||
|
||||
-- Helper data ================================================================
|
||||
-- Module default config
|
||||
H.default_config = vim.deepcopy(MiniPairs.config)
|
||||
|
||||
-- Default value of `pair_info` for mapping functions
|
||||
H.default_pair_info = { neigh_pattern = '..', register = { bs = true, cr = true } }
|
||||
|
||||
-- Pair sets registered *per mode-buffer-key*. Buffer `'all'` contains pairs
|
||||
-- registered for all buffers.
|
||||
H.registered_pairs = {
|
||||
i = { all = { bs = {}, cr = {} } },
|
||||
c = { all = { bs = {}, cr = {} } },
|
||||
t = { all = { bs = {}, cr = {} } },
|
||||
}
|
||||
|
||||
-- Precomputed keys to increase speed
|
||||
-- stylua: ignore start
|
||||
local function escape(s) return vim.api.nvim_replace_termcodes(s, true, true, true) end
|
||||
H.keys = {
|
||||
above = escape('<C-o>O'),
|
||||
bs = escape('<bs>'),
|
||||
cr = escape('<cr>'),
|
||||
del = escape('<del>'),
|
||||
keep_undo = escape('<C-g>U'),
|
||||
-- NOTE: use `get_arrow_key()` instead of `H.keys.left` or `H.keys.right`
|
||||
left = escape('<left>'),
|
||||
right = escape('<right>')
|
||||
}
|
||||
-- stylua: ignore end
|
||||
|
||||
-- Helper functionality =======================================================
|
||||
-- Settings -------------------------------------------------------------------
|
||||
H.setup_config = function(config)
|
||||
-- General idea: if some table elements are not present in user-supplied
|
||||
-- `config`, take them from default config
|
||||
vim.validate({ config = { config, 'table', true } })
|
||||
config = vim.tbl_deep_extend('force', vim.deepcopy(H.default_config), config or {})
|
||||
|
||||
-- Validate per nesting level to produce correct error message
|
||||
vim.validate({
|
||||
modes = { config.modes, 'table' },
|
||||
mappings = { config.mappings, 'table' },
|
||||
})
|
||||
|
||||
vim.validate({
|
||||
['modes.insert'] = { config.modes.insert, 'boolean' },
|
||||
['modes.command'] = { config.modes.command, 'boolean' },
|
||||
['modes.terminal'] = { config.modes.terminal, 'boolean' },
|
||||
})
|
||||
|
||||
local validate_mapping = function(pair_info, prefix)
|
||||
-- Allow `false` to not create mapping
|
||||
if pair_info == false then return end
|
||||
H.validate_pair_info(pair_info, prefix)
|
||||
end
|
||||
|
||||
validate_mapping(config.mappings['('], "mappings['(']")
|
||||
validate_mapping(config.mappings['['], "mappings['[']")
|
||||
validate_mapping(config.mappings['{'], "mappings['{']")
|
||||
validate_mapping(config.mappings[')'], "mappings[')']")
|
||||
validate_mapping(config.mappings[']'], "mappings[']']")
|
||||
validate_mapping(config.mappings['}'], "mappings['}']")
|
||||
validate_mapping(config.mappings['"'], "mappings['\"']")
|
||||
validate_mapping(config.mappings["'"], 'mappings["\'"]')
|
||||
validate_mapping(config.mappings['`'], "mappings['`']")
|
||||
|
||||
return config
|
||||
end
|
||||
|
||||
H.apply_config = function(config)
|
||||
MiniPairs.config = config
|
||||
|
||||
-- Setup mappings in supplied modes
|
||||
local mode_ids = { insert = 'i', command = 'c', terminal = 't' }
|
||||
-- Compute in which modes mapping should be set up
|
||||
local mode_array = {}
|
||||
for name, to_set in pairs(config.modes) do
|
||||
if to_set then table.insert(mode_array, mode_ids[name]) end
|
||||
end
|
||||
|
||||
local map_conditionally = function(mode, key, pair_info)
|
||||
-- Allow `false` to not create mapping
|
||||
if pair_info == false then return end
|
||||
|
||||
-- This also should take care of mapping `<BS>` and `<CR>`
|
||||
MiniPairs.map(mode, key, pair_info)
|
||||
end
|
||||
|
||||
for _, mode in pairs(mode_array) do
|
||||
for key, pair_info in pairs(config.mappings) do
|
||||
map_conditionally(mode, key, pair_info)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
H.create_autocommands = function()
|
||||
local augroup = vim.api.nvim_create_augroup('MiniPairs', {})
|
||||
|
||||
local au = function(event, pattern, callback, desc)
|
||||
vim.api.nvim_create_autocmd(event, { group = augroup, pattern = pattern, callback = callback, desc = desc })
|
||||
end
|
||||
|
||||
au('FileType', { 'TelescopePrompt', 'fzf' }, function() vim.b.minipairs_disable = true end, 'Disable locally')
|
||||
end
|
||||
|
||||
H.is_disabled = function() return vim.g.minipairs_disable == true or vim.b.minipairs_disable == true end
|
||||
|
||||
-- Pair registration ----------------------------------------------------------
|
||||
H.register_pair = function(pair_info, mode, buffer)
|
||||
-- Process new mode
|
||||
H.registered_pairs[mode] = H.registered_pairs[mode] or { all = { bs = {}, cr = {} } }
|
||||
local mode_pairs = H.registered_pairs[mode]
|
||||
|
||||
-- Process new buffer
|
||||
mode_pairs[buffer] = mode_pairs[buffer] or { bs = {}, cr = {} }
|
||||
|
||||
-- Register pair if it is not already registered
|
||||
local register, pair = pair_info.register, pair_info.pair
|
||||
if register.bs and not vim.tbl_contains(mode_pairs[buffer].bs, pair) then
|
||||
table.insert(mode_pairs[buffer].bs, pair)
|
||||
end
|
||||
if register.cr and not vim.tbl_contains(mode_pairs[buffer].cr, pair) then
|
||||
table.insert(mode_pairs[buffer].cr, pair)
|
||||
end
|
||||
end
|
||||
|
||||
H.unregister_pair = function(pair, mode, buffer)
|
||||
local mode_pairs = H.registered_pairs[mode]
|
||||
if not (mode_pairs and mode_pairs[buffer]) then return end
|
||||
|
||||
local buf_pairs = mode_pairs[buffer]
|
||||
for _, key in ipairs({ 'bs', 'cr' }) do
|
||||
for i, p in ipairs(buf_pairs[key]) do
|
||||
if p == pair then table.remove(buf_pairs[key], i) end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
H.is_pair_registered = function(pair, mode, buffer, key)
|
||||
local mode_pairs = H.registered_pairs[mode]
|
||||
if not mode_pairs then return false end
|
||||
|
||||
if vim.tbl_contains(mode_pairs['all'][key], pair) then return true end
|
||||
|
||||
buffer = buffer == 0 and vim.api.nvim_get_current_buf() or buffer
|
||||
local buf_pairs = mode_pairs[buffer]
|
||||
if not buf_pairs then return false end
|
||||
|
||||
return vim.tbl_contains(buf_pairs[key], pair)
|
||||
end
|
||||
|
||||
H.ensure_cr_bs = function(mode)
|
||||
local has_any_cr_pair, has_any_bs_pair = false, false
|
||||
for _, pair_tbl in pairs(H.registered_pairs[mode]) do
|
||||
has_any_cr_pair = has_any_cr_pair or not vim.tbl_isempty(pair_tbl.cr)
|
||||
has_any_bs_pair = has_any_bs_pair or not vim.tbl_isempty(pair_tbl.bs)
|
||||
end
|
||||
|
||||
-- NOTE: this doesn't distinguish between global and buffer mappings. Both
|
||||
-- `<BS>` and `<CR>` should work as normal even if no pairs are registered
|
||||
if has_any_bs_pair then
|
||||
-- Use not `silent` in Command mode to make it redraw
|
||||
local opts = { silent = mode ~= 'c', expr = true, replace_keycodes = false, desc = 'MiniPairs <BS>' }
|
||||
H.map(mode, '<BS>', 'v:lua.MiniPairs.bs()', opts)
|
||||
end
|
||||
if mode == 'i' and has_any_cr_pair then
|
||||
local opts = { expr = true, replace_keycodes = false, desc = 'MiniPairs <CR>' }
|
||||
H.map(mode, '<CR>', 'v:lua.MiniPairs.cr()', opts)
|
||||
end
|
||||
end
|
||||
|
||||
-- Work with pair_info --------------------------------------------------------
|
||||
H.validate_pair_info = function(pair_info, prefix)
|
||||
prefix = prefix or 'pair_info'
|
||||
vim.validate({ [prefix] = { pair_info, 'table' } })
|
||||
pair_info = vim.tbl_deep_extend('force', H.default_pair_info, pair_info)
|
||||
|
||||
vim.validate({
|
||||
[prefix .. '.action'] = { pair_info.action, 'string' },
|
||||
[prefix .. '.pair'] = { pair_info.pair, 'string' },
|
||||
[prefix .. '.neigh_pattern'] = { pair_info.neigh_pattern, 'string' },
|
||||
[prefix .. '.register'] = { pair_info.register, 'table' },
|
||||
})
|
||||
|
||||
vim.validate({
|
||||
[prefix .. '.register.bs'] = { pair_info.register.bs, 'boolean' },
|
||||
[prefix .. '.register.cr'] = { pair_info.register.cr, 'boolean' },
|
||||
})
|
||||
|
||||
return pair_info
|
||||
end
|
||||
|
||||
H.pair_info_to_map_rhs = function(pair_info)
|
||||
return ('v:lua.MiniPairs.%s(%s, %s)'):format(
|
||||
pair_info.action,
|
||||
vim.inspect(pair_info.pair),
|
||||
vim.inspect(pair_info.neigh_pattern)
|
||||
)
|
||||
end
|
||||
|
||||
H.infer_mapping_description = function(pair_info)
|
||||
local action_name = pair_info.action:sub(1, 1):upper() .. pair_info.action:sub(2)
|
||||
return ('%s action for %s pair'):format(action_name, vim.inspect(pair_info.pair))
|
||||
end
|
||||
|
||||
-- Utilities ------------------------------------------------------------------
|
||||
H.get_cursor_neigh = function(start, finish)
|
||||
local line, col
|
||||
if vim.fn.mode() == 'c' then
|
||||
line = vim.fn.getcmdline()
|
||||
col = vim.fn.getcmdpos()
|
||||
-- Adjust start and finish because output of `getcmdpos()` starts counting
|
||||
-- columns from 1
|
||||
start = start - 1
|
||||
finish = finish - 1
|
||||
else
|
||||
line = vim.api.nvim_get_current_line()
|
||||
col = vim.api.nvim_win_get_cursor(0)[2]
|
||||
end
|
||||
|
||||
-- Add '\r' and '\n' to always return 2 characters
|
||||
return string.sub(('%s%s%s'):format('\r', line, '\n'), col + 1 + start, col + 1 + finish)
|
||||
end
|
||||
|
||||
H.neigh_match = function(pattern) return (pattern == nil) or (H.get_cursor_neigh(0, 1):find(pattern) ~= nil) end
|
||||
|
||||
H.get_arrow_key = function(key)
|
||||
if vim.fn.mode() == 'i' then
|
||||
-- Using left/right keys in insert mode breaks undo sequence and, more
|
||||
-- importantly, dot-repeat. To avoid this, use 'i_CTRL-G_U' mapping.
|
||||
return H.keys.keep_undo .. H.keys[key]
|
||||
else
|
||||
return H.keys[key]
|
||||
end
|
||||
end
|
||||
|
||||
H.map = function(mode, lhs, rhs, opts)
|
||||
if lhs == '' then return end
|
||||
opts = vim.tbl_deep_extend('force', { silent = true }, opts or {})
|
||||
vim.keymap.set(mode, lhs, rhs, opts)
|
||||
end
|
||||
|
||||
H.restore_eventignore = vim.schedule_wrap(function(val) vim.o.eventignore = val end)
|
||||
|
||||
return MiniPairs
|
||||
3427
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/pick.lua
Normal file
3427
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/pick.lua
Normal file
File diff suppressed because it is too large
Load Diff
643
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/sessions.lua
Normal file
643
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/sessions.lua
Normal file
@ -0,0 +1,643 @@
|
||||
--- *mini.sessions* Session management
|
||||
--- *MiniSessions*
|
||||
---
|
||||
--- MIT License Copyright (c) 2021 Evgeni Chasnovski
|
||||
---
|
||||
--- ==============================================================================
|
||||
---
|
||||
--- Read, write, and delete sessions. Works using |mksession| (meaning
|
||||
--- 'sessionoptions' is fully respected). This is intended as a drop-in Lua
|
||||
--- replacement for session management part of 'mhinz/vim-startify' (works out
|
||||
--- of the box with sessions created by it). Implements both global (from
|
||||
--- configured directory) and local (from current directory) sessions.
|
||||
---
|
||||
--- Key design ideas:
|
||||
--- - Sessions are represented by readable files (results of applying
|
||||
--- |mksession|). There are two kinds of sessions:
|
||||
--- - Global: any file inside a configurable directory.
|
||||
--- - Local: configurable file inside current working directory (|getcwd|).
|
||||
---
|
||||
--- - All session files are detected during `MiniSessions.setup()` and on any
|
||||
--- relevant action with session names being file names (including their
|
||||
--- possible extension).
|
||||
---
|
||||
--- - Store information about detected sessions in separate table
|
||||
--- (|MiniSessions.detected|) and operate only on it. Meaning if this
|
||||
--- information changes, there will be no effect until next detection. So to
|
||||
--- avoid confusion, don't directly use |mksession| and |source| for writing
|
||||
--- and reading sessions files.
|
||||
---
|
||||
--- Features:
|
||||
--- - Autoread default session (local if detected, latest otherwise) if Neovim
|
||||
--- was called without intention to show something else.
|
||||
---
|
||||
--- - Autowrite current session before quitting Neovim.
|
||||
---
|
||||
--- - Configurable severity level of all actions.
|
||||
---
|
||||
--- # Setup ~
|
||||
---
|
||||
--- This module needs a setup with `require('mini.sessions').setup({})`
|
||||
--- (replace `{}` with your `config` table). It will create global Lua table
|
||||
--- `MiniSessions` which you can use for scripting or manually (with
|
||||
--- `:lua MiniSessions.*`).
|
||||
---
|
||||
--- See |MiniSessions.config| for `config` structure and default values.
|
||||
---
|
||||
--- This module doesn't benefit from buffer local configuration, so using
|
||||
--- `vim.b.minisessions_config` will have no effect here.
|
||||
---
|
||||
--- # Disabling ~
|
||||
---
|
||||
--- To disable core functionality, set `vim.g.minisessions_disable` (globally) or
|
||||
--- `vim.b.minisessions_disable` (for a buffer) to `true`. Considering high
|
||||
--- number of different scenarios and customization intentions, writing exact
|
||||
--- rules for disabling module's functionality is left to user. See
|
||||
--- |mini.nvim-disabling-recipes| for common recipes.
|
||||
|
||||
-- Module definition ==========================================================
|
||||
local MiniSessions = {}
|
||||
local H = {}
|
||||
|
||||
--- Module setup
|
||||
---
|
||||
---@param config table|nil Module config table. See |MiniSessions.config|.
|
||||
---
|
||||
---@usage >lua
|
||||
--- require('mini.sessions').setup() -- use default config
|
||||
--- -- OR
|
||||
--- require('mini.sessions').setup({}) -- replace {} with your config table
|
||||
--- <
|
||||
MiniSessions.setup = function(config)
|
||||
-- Export module
|
||||
_G.MiniSessions = MiniSessions
|
||||
|
||||
-- Setup config
|
||||
config = H.setup_config(config)
|
||||
|
||||
-- Apply config
|
||||
H.apply_config(config)
|
||||
|
||||
-- Define behavior
|
||||
H.create_autocommands(config)
|
||||
end
|
||||
|
||||
--- Module config
|
||||
---
|
||||
--- Default values:
|
||||
---@eval return MiniDoc.afterlines_to_code(MiniDoc.current.eval_section)
|
||||
MiniSessions.config = {
|
||||
-- Whether to read latest session if Neovim opened without file arguments
|
||||
autoread = false,
|
||||
|
||||
-- Whether to write current session before quitting Neovim
|
||||
autowrite = true,
|
||||
|
||||
-- Directory where global sessions are stored (use `''` to disable)
|
||||
--minidoc_replace_start directory = --<"session" subdir of user data directory from |stdpath()|>,
|
||||
directory = vim.fn.stdpath('data') .. '/session',
|
||||
--minidoc_replace_end
|
||||
|
||||
-- File for local session (use `''` to disable)
|
||||
file = 'Session.vim',
|
||||
|
||||
-- Whether to force possibly harmful actions (meaning depends on function)
|
||||
force = { read = false, write = true, delete = false },
|
||||
|
||||
-- Hook functions for actions. Default `nil` means 'do nothing'.
|
||||
-- Takes table with active session data as argument.
|
||||
hooks = {
|
||||
-- Before successful action
|
||||
pre = { read = nil, write = nil, delete = nil },
|
||||
-- After successful action
|
||||
post = { read = nil, write = nil, delete = nil },
|
||||
},
|
||||
|
||||
-- Whether to print session path after action
|
||||
verbose = { read = false, write = true, delete = true },
|
||||
}
|
||||
--minidoc_afterlines_end
|
||||
|
||||
-- Module data ================================================================
|
||||
--- Table of detected sessions. Keys represent session name. Values are tables
|
||||
--- with session information that currently has these fields (but subject to
|
||||
--- change):
|
||||
--- - <modify_time> `(number)` modification time (see |getftime|) of session file.
|
||||
--- - <name> `(string)` name of session (should be equal to table key).
|
||||
--- - <path> `(string)` full path to session file.
|
||||
--- - <type> `(string)` type of session ('global' or 'local').
|
||||
MiniSessions.detected = {}
|
||||
|
||||
-- Module functionality =======================================================
|
||||
--- Read detected session
|
||||
---
|
||||
--- What it does:
|
||||
--- - If there is an active session, write it with |MiniSessions.write()|.
|
||||
--- - Delete all current buffers with |bwipeout|. This is needed to correctly
|
||||
--- restore buffers from target session. If `force` is not `true`, checks
|
||||
--- beforehand for unsaved listed buffers and stops if there is any.
|
||||
--- - Source session with supplied name.
|
||||
---
|
||||
---@param session_name string|nil Name of detected session file to read. Default:
|
||||
--- `nil` for default session: local (if detected) or latest session (see
|
||||
--- |MiniSessions.get_latest|).
|
||||
---@param opts table|nil Table with options. Current allowed keys:
|
||||
--- - <force> (whether to delete unsaved buffers; default:
|
||||
--- `MiniSessions.config.force.read`).
|
||||
--- - <verbose> (whether to print session path after action; default
|
||||
--- `MiniSessions.config.verbose.read`).
|
||||
--- - <hooks> (a table with <pre> and <post> function hooks to be executed
|
||||
--- with session data argument before and after successful read; overrides
|
||||
--- `MiniSessions.config.hooks.pre.read` and
|
||||
--- `MiniSessions.config.hooks.post.read`).
|
||||
MiniSessions.read = function(session_name, opts)
|
||||
if H.is_disabled() then return end
|
||||
|
||||
-- Make sessions up to date
|
||||
H.detect_sessions()
|
||||
if vim.tbl_count(MiniSessions.detected) == 0 then
|
||||
return H.message('There is no detected sessions. Change configuration and rerun `MiniSessions.setup()`.')
|
||||
end
|
||||
|
||||
-- Get session data
|
||||
if session_name == nil then
|
||||
if MiniSessions.detected[MiniSessions.config.file] ~= nil then
|
||||
session_name = MiniSessions.config.file
|
||||
else
|
||||
session_name = MiniSessions.get_latest()
|
||||
end
|
||||
end
|
||||
|
||||
opts = vim.tbl_deep_extend('force', H.default_opts('read'), opts or {})
|
||||
|
||||
if not H.validate_detected(session_name) then return end
|
||||
|
||||
local data = MiniSessions.detected[session_name]
|
||||
|
||||
-- Possibly check for unsaved listed buffers and do nothing if present
|
||||
if not opts.force then
|
||||
local unsaved_listed_buffers = H.get_unsaved_listed_buffers()
|
||||
|
||||
if #unsaved_listed_buffers > 0 then
|
||||
local buf_list = table.concat(unsaved_listed_buffers, ', ')
|
||||
H.error(('There are unsaved listed buffers: %s.'):format(buf_list))
|
||||
end
|
||||
end
|
||||
|
||||
-- Write current session to allow proper switching between sessions
|
||||
if vim.v.this_session ~= '' then MiniSessions.write(nil, { force = true, verbose = false }) end
|
||||
|
||||
-- Execute 'pre' hook
|
||||
H.possibly_execute(opts.hooks.pre, data)
|
||||
|
||||
-- Wipeout all buffers
|
||||
vim.cmd('silent! %bwipeout!')
|
||||
|
||||
-- Read session file
|
||||
local session_path = data.path
|
||||
vim.cmd(('silent! source %s'):format(vim.fn.fnameescape(session_path)))
|
||||
vim.v.this_session = session_path
|
||||
|
||||
-- Possibly notify
|
||||
if opts.verbose then H.message(('Read session %s'):format(session_path)) end
|
||||
|
||||
-- Execute 'post' hook
|
||||
H.possibly_execute(opts.hooks.post, data)
|
||||
end
|
||||
|
||||
--- Write session
|
||||
---
|
||||
--- What it does:
|
||||
--- - Check if file for supplied session name already exists. If it does and
|
||||
--- `force` is not `true`, then stop.
|
||||
--- - Write session with |mksession| to a file named `session_name`. Its
|
||||
--- directory is determined based on type of session:
|
||||
--- - It is at location |v:this_session| if `session_name` is `nil` and
|
||||
--- there is current session.
|
||||
--- - It is current working directory (|getcwd|) if `session_name` is equal
|
||||
--- to `MiniSessions.config.file` (represents local session).
|
||||
--- - It is `MiniSessions.config.directory` otherwise (represents global
|
||||
--- session).
|
||||
--- - Update |MiniSessions.detected|.
|
||||
---
|
||||
---@param session_name string|nil Name of session file to write. Default: `nil` for
|
||||
--- current session (|v:this_session|).
|
||||
---@param opts table|nil Table with options. Current allowed keys:
|
||||
--- - <force> (whether to ignore existence of session file; default:
|
||||
--- `MiniSessions.config.force.write`).
|
||||
--- - <verbose> (whether to print session path after action; default
|
||||
--- `MiniSessions.config.verbose.write`).
|
||||
--- - <hooks> (a table with <pre> and <post> function hooks to be executed
|
||||
--- with session data argument before and after successful write; overrides
|
||||
--- `MiniSessions.config.hooks.pre.write` and
|
||||
--- `MiniSessions.config.hooks.post.write`).
|
||||
MiniSessions.write = function(session_name, opts)
|
||||
if H.is_disabled() then return end
|
||||
|
||||
opts = vim.tbl_deep_extend('force', H.default_opts('write'), opts or {})
|
||||
|
||||
local session_path = H.name_to_path(session_name)
|
||||
|
||||
if not opts.force and H.is_readable_file(session_path) then
|
||||
H.error([[Can't write to existing session when `opts.force` is not `true`.]])
|
||||
end
|
||||
|
||||
local data = H.new_session(session_path)
|
||||
|
||||
-- Execute 'pre' hook
|
||||
H.possibly_execute(opts.hooks.pre, data)
|
||||
|
||||
-- Make session file
|
||||
local cmd = ('mksession%s'):format(opts.force and '!' or '')
|
||||
vim.cmd(('%s %s'):format(cmd, vim.fn.fnameescape(session_path)))
|
||||
data.modify_time = vim.fn.getftime(session_path)
|
||||
|
||||
-- Update detected sessions
|
||||
MiniSessions.detected[data.name] = data
|
||||
|
||||
-- Possibly notify
|
||||
if opts.verbose then H.message(('Written session %s'):format(session_path)) end
|
||||
|
||||
-- Execute 'post' hook
|
||||
H.possibly_execute(opts.hooks.post, data)
|
||||
end
|
||||
|
||||
--- Delete detected session
|
||||
---
|
||||
--- What it does:
|
||||
--- - Check if session name is a current one. If yes and `force` is not `true`,
|
||||
--- then stop.
|
||||
--- - Delete session.
|
||||
--- - Update |MiniSessions.detected|.
|
||||
---
|
||||
---@param session_name string|nil Name of detected session file to delete. Default:
|
||||
--- `nil` for name of current session (taken from |v:this_session|).
|
||||
---@param opts table|nil Table with options. Current allowed keys:
|
||||
--- - <force> (whether to allow deletion of current session; default:
|
||||
--- `MiniSessions.config.force.delete`).
|
||||
--- - <verbose> (whether to print session path after action; default
|
||||
--- `MiniSessions.config.verbose.delete`).
|
||||
--- - <hooks> (a table with <pre> and <post> function hooks to be executed
|
||||
--- with session data argument before and after successful delete; overrides
|
||||
--- `MiniSessions.config.hooks.pre.delete` and
|
||||
--- `MiniSessions.config.hooks.post.delete`).
|
||||
MiniSessions.delete = function(session_name, opts)
|
||||
if H.is_disabled() then return end
|
||||
if vim.tbl_count(MiniSessions.detected) == 0 then
|
||||
H.error('There is no detected sessions. Change configuration and rerun `MiniSessions.setup()`.')
|
||||
end
|
||||
|
||||
opts = vim.tbl_deep_extend('force', H.default_opts('delete'), opts or {})
|
||||
|
||||
local session_path = H.name_to_path(session_name)
|
||||
|
||||
-- Make sessions up to date
|
||||
H.detect_sessions()
|
||||
|
||||
-- Make sure to delete only detected session (matters for local session)
|
||||
session_name = vim.fn.fnamemodify(session_path, ':t')
|
||||
if not H.validate_detected(session_name) then return end
|
||||
session_path = MiniSessions.detected[session_name].path
|
||||
|
||||
local is_current_session = session_path == vim.v.this_session
|
||||
if not opts.force and is_current_session then
|
||||
H.error([[Can't delete current session when `opts.force` is not `true`.]])
|
||||
end
|
||||
|
||||
local data = MiniSessions.detected[session_name]
|
||||
|
||||
-- Execute 'pre' hook
|
||||
H.possibly_execute(opts.hooks.pre, data)
|
||||
|
||||
-- Delete and update detected sessions
|
||||
vim.fn.delete(session_path)
|
||||
MiniSessions.detected[session_name] = nil
|
||||
if is_current_session then vim.v.this_session = '' end
|
||||
|
||||
-- Possibly notify
|
||||
if opts.verbose then H.message(('Deleted session %s'):format(session_path)) end
|
||||
|
||||
-- Execute 'pre' hook
|
||||
H.possibly_execute(opts.hooks.post, data)
|
||||
end
|
||||
|
||||
--- Select session interactively and perform action
|
||||
---
|
||||
--- Note: this uses |vim.ui.select()| function. For more user-friendly
|
||||
--- experience, override it (for example, with external plugins like
|
||||
--- "stevearc/dressing.nvim").
|
||||
---
|
||||
---@param action string|nil Action to perform. Should be one of "read" (default),
|
||||
--- "write", or "delete".
|
||||
---@param opts table|nil Options for specified action.
|
||||
MiniSessions.select = function(action, opts)
|
||||
if not (type(vim.ui) == 'table' and type(vim.ui.select) == 'function') then
|
||||
H.error('`MiniSessions.select()` requires `vim.ui.select()` function.')
|
||||
end
|
||||
|
||||
action = action or 'read'
|
||||
if not vim.tbl_contains({ 'read', 'write', 'delete' }, action) then
|
||||
H.error("`action` should be one of 'read', 'write', or 'delete'.")
|
||||
end
|
||||
|
||||
-- Make sessions up to date
|
||||
H.detect_sessions()
|
||||
|
||||
-- Ensure consistent order of items
|
||||
local detected = {}
|
||||
for _, session in pairs(MiniSessions.detected) do
|
||||
table.insert(detected, session)
|
||||
end
|
||||
local sort_fun = function(a, b)
|
||||
-- Put local session first, others - increasing alphabetically
|
||||
local a_name = a.type == 'local' and '' or a.name
|
||||
local b_name = b.type == 'local' and '' or b.name
|
||||
return a_name < b_name
|
||||
end
|
||||
table.sort(detected, sort_fun)
|
||||
local detected_names = vim.tbl_map(function(x) return x.name end, detected)
|
||||
|
||||
vim.ui.select(detected_names, {
|
||||
prompt = 'Select session to ' .. action,
|
||||
format_item = function(x) return ('%s (%s)'):format(x, MiniSessions.detected[x].type) end,
|
||||
}, function(item, idx)
|
||||
if item == nil then return end
|
||||
MiniSessions[action](item, opts)
|
||||
end)
|
||||
end
|
||||
|
||||
--- Get name of latest detected session
|
||||
---
|
||||
--- Latest session is the session with the latest modification time determined
|
||||
--- by |getftime|.
|
||||
---
|
||||
---@return string|nil Name of latest session or `nil` if there is no sessions.
|
||||
MiniSessions.get_latest = function()
|
||||
if vim.tbl_count(MiniSessions.detected) == 0 then return end
|
||||
|
||||
local latest_time, latest_name = -1, nil
|
||||
for name, data in pairs(MiniSessions.detected) do
|
||||
if data.modify_time > latest_time then
|
||||
latest_time, latest_name = data.modify_time, name
|
||||
end
|
||||
end
|
||||
|
||||
return latest_name
|
||||
end
|
||||
|
||||
-- Helper data ================================================================
|
||||
-- Module default config
|
||||
H.default_config = vim.deepcopy(MiniSessions.config)
|
||||
|
||||
-- Helper functionality =======================================================
|
||||
-- Settings -------------------------------------------------------------------
|
||||
H.setup_config = function(config)
|
||||
-- General idea: if some table elements are not present in user-supplied
|
||||
-- `config`, take them from default config
|
||||
vim.validate({ config = { config, 'table', true } })
|
||||
config = vim.tbl_deep_extend('force', vim.deepcopy(H.default_config), config or {})
|
||||
|
||||
-- Validate per nesting level to produce correct error message
|
||||
vim.validate({
|
||||
autoread = { config.autoread, 'boolean' },
|
||||
autowrite = { config.autowrite, 'boolean' },
|
||||
directory = { config.directory, 'string' },
|
||||
file = { config.file, 'string' },
|
||||
force = { config.force, 'table' },
|
||||
hooks = { config.hooks, 'table' },
|
||||
verbose = { config.verbose, 'table' },
|
||||
})
|
||||
|
||||
vim.validate({
|
||||
['force.read'] = { config.force.read, 'boolean' },
|
||||
['force.write'] = { config.force.write, 'boolean' },
|
||||
['force.delete'] = { config.force.delete, 'boolean' },
|
||||
|
||||
['hooks.pre'] = { config.hooks.pre, 'table' },
|
||||
['hooks.post'] = { config.hooks.post, 'table' },
|
||||
|
||||
['verbose.read'] = { config.verbose.read, 'boolean' },
|
||||
['verbose.write'] = { config.verbose.write, 'boolean' },
|
||||
['verbose.delete'] = { config.verbose.delete, 'boolean' },
|
||||
})
|
||||
|
||||
vim.validate({
|
||||
['hooks.pre.read'] = { config.hooks.pre.read, 'function', true },
|
||||
['hooks.pre.write'] = { config.hooks.pre.write, 'function', true },
|
||||
['hooks.pre.delete'] = { config.hooks.pre.delete, 'function', true },
|
||||
|
||||
['hooks.post.read'] = { config.hooks.post.read, 'function', true },
|
||||
['hooks.post.write'] = { config.hooks.post.write, 'function', true },
|
||||
['hooks.post.delete'] = { config.hooks.post.delete, 'function', true },
|
||||
})
|
||||
|
||||
return config
|
||||
end
|
||||
|
||||
H.apply_config = function(config)
|
||||
MiniSessions.config = config
|
||||
|
||||
H.detect_sessions(config)
|
||||
end
|
||||
|
||||
H.create_autocommands = function(config)
|
||||
local augroup = vim.api.nvim_create_augroup('MiniSessions', {})
|
||||
|
||||
if config.autoread then
|
||||
local autoread = function()
|
||||
if not H.is_something_shown() then MiniSessions.read() end
|
||||
end
|
||||
local opts = { group = augroup, nested = true, once = true, callback = autoread, desc = 'Autoread latest session' }
|
||||
vim.api.nvim_create_autocmd('VimEnter', opts)
|
||||
end
|
||||
|
||||
if config.autowrite then
|
||||
local autowrite = function()
|
||||
if vim.v.this_session ~= '' then MiniSessions.write(nil, { force = true }) end
|
||||
end
|
||||
vim.api.nvim_create_autocmd(
|
||||
'VimLeavePre',
|
||||
{ group = augroup, callback = autowrite, desc = 'Autowrite current session' }
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
H.is_disabled = function() return vim.g.minisessions_disable == true or vim.b.minisessions_disable == true end
|
||||
|
||||
H.get_config = function(config)
|
||||
return vim.tbl_deep_extend('force', MiniSessions.config, vim.b.minisessions_config or {}, config or {})
|
||||
end
|
||||
|
||||
-- Work with sessions ---------------------------------------------------------
|
||||
H.detect_sessions = function(config)
|
||||
config = H.get_config(config)
|
||||
|
||||
local res_global = config.directory == '' and {} or H.detect_sessions_global(config.directory)
|
||||
local res_local = config.file == '' and {} or H.detect_sessions_local(config.file)
|
||||
|
||||
-- If there are both local and global session with same name, prefer local
|
||||
MiniSessions.detected = vim.tbl_deep_extend('force', res_global, res_local)
|
||||
end
|
||||
|
||||
H.detect_sessions_global = function(global_dir)
|
||||
-- Ensure correct directory path: create if doesn't exist
|
||||
global_dir = H.full_path(global_dir)
|
||||
if vim.fn.isdirectory(global_dir) ~= 1 then
|
||||
local ok, _ = pcall(vim.fn.mkdir, global_dir, 'p')
|
||||
|
||||
if not ok then
|
||||
H.message(('%s is not a directory path.'):format(vim.inspect(global_dir)))
|
||||
return {}
|
||||
end
|
||||
end
|
||||
|
||||
-- Find global sessions
|
||||
local globs = vim.fn.globpath(global_dir, '*')
|
||||
if #globs == 0 then return {} end
|
||||
|
||||
local res = {}
|
||||
for _, f in pairs(vim.split(globs, '\n')) do
|
||||
if H.is_readable_file(f) then
|
||||
local s = H.new_session(f, 'global')
|
||||
res[s.name] = s
|
||||
end
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
H.detect_sessions_local = function(local_file)
|
||||
local f = H.join_path(vim.fn.getcwd(), local_file)
|
||||
|
||||
if not H.is_readable_file(f) then return {} end
|
||||
|
||||
local res = {}
|
||||
local s = H.new_session(f, 'local')
|
||||
res[s.name] = s
|
||||
return res
|
||||
end
|
||||
|
||||
H.new_session = function(session_path, session_type)
|
||||
return {
|
||||
modify_time = vim.fn.getftime(session_path),
|
||||
name = vim.fn.fnamemodify(session_path, ':t'),
|
||||
path = H.full_path(session_path),
|
||||
type = session_type or H.get_session_type(session_path),
|
||||
}
|
||||
end
|
||||
|
||||
H.get_session_type = function(session_path)
|
||||
if MiniSessions.config.directory == '' then return 'local' end
|
||||
|
||||
local session_dir = H.full_path(session_path)
|
||||
local global_dir = H.full_path(MiniSessions.config.directory)
|
||||
return session_dir == global_dir and 'global' or 'local'
|
||||
end
|
||||
|
||||
H.validate_detected = function(session_name)
|
||||
local is_detected = vim.tbl_contains(vim.tbl_keys(MiniSessions.detected), session_name)
|
||||
if is_detected then return true end
|
||||
|
||||
H.error(('%s is not a name for detected session.'):format(vim.inspect(session_name)))
|
||||
end
|
||||
|
||||
H.get_unsaved_listed_buffers = function()
|
||||
return vim.tbl_filter(
|
||||
function(buf_id) return vim.bo[buf_id].modified and vim.bo[buf_id].buflisted end,
|
||||
vim.api.nvim_list_bufs()
|
||||
)
|
||||
end
|
||||
|
||||
H.get_current_session_name = function() return vim.fn.fnamemodify(vim.v.this_session, ':t') end
|
||||
|
||||
H.name_to_path = function(session_name)
|
||||
if session_name == nil then
|
||||
if vim.v.this_session == '' then H.error('There is no active session. Supply non-nil session name.') end
|
||||
return vim.v.this_session
|
||||
end
|
||||
|
||||
session_name = tostring(session_name)
|
||||
if session_name == '' then H.error('Supply non-empty session name.') end
|
||||
|
||||
local session_dir = (session_name == MiniSessions.config.file) and vim.fn.getcwd() or MiniSessions.config.directory
|
||||
local path = H.join_path(session_dir, session_name)
|
||||
return H.full_path(path)
|
||||
end
|
||||
|
||||
-- Utilities ------------------------------------------------------------------
|
||||
H.echo = function(msg, is_important)
|
||||
-- Construct message chunks
|
||||
msg = type(msg) == 'string' and { { msg } } or msg
|
||||
table.insert(msg, 1, { '(mini.sessions) ', 'WarningMsg' })
|
||||
|
||||
-- Avoid hit-enter-prompt
|
||||
local max_width = vim.o.columns * math.max(vim.o.cmdheight - 1, 0) + vim.v.echospace
|
||||
local chunks, tot_width = {}, 0
|
||||
for _, ch in ipairs(msg) do
|
||||
local new_ch = { vim.fn.strcharpart(ch[1], 0, max_width - tot_width), ch[2] }
|
||||
table.insert(chunks, new_ch)
|
||||
tot_width = tot_width + vim.fn.strdisplaywidth(new_ch[1])
|
||||
if tot_width >= max_width then break end
|
||||
end
|
||||
|
||||
-- Echo. Force redraw to ensure that it is effective (`:h echo-redraw`)
|
||||
vim.cmd([[echo '' | redraw]])
|
||||
vim.api.nvim_echo(chunks, is_important, {})
|
||||
end
|
||||
|
||||
H.message = function(msg) H.echo(msg, true) end
|
||||
|
||||
H.error = function(msg) error(('(mini.sessions) %s'):format(msg)) end
|
||||
|
||||
H.default_opts = function(action)
|
||||
local config = MiniSessions.config
|
||||
return {
|
||||
force = config.force[action],
|
||||
verbose = config.verbose[action],
|
||||
hooks = { pre = config.hooks.pre[action], post = config.hooks.post[action] },
|
||||
}
|
||||
end
|
||||
|
||||
H.is_readable_file = function(path) return vim.fn.isdirectory(path) ~= 1 and vim.fn.getfperm(path):sub(1, 1) == 'r' end
|
||||
|
||||
H.join_path = function(directory, filename)
|
||||
return (string.format('%s/%s', directory, filename):gsub('\\', '/'):gsub('/+', '/'))
|
||||
end
|
||||
|
||||
H.full_path = function(path) return vim.fn.resolve(vim.fn.fnamemodify(path, ':p')) end
|
||||
|
||||
H.is_something_shown = function()
|
||||
-- Don't autoread session if Neovim is opened to show something. That is
|
||||
-- when at least one of the following is true:
|
||||
-- - There are files in arguments (like `nvim foo.txt` with new file).
|
||||
if vim.fn.argc() > 0 then return true end
|
||||
|
||||
-- - Several buffers are listed (like session with placeholder buffers). That
|
||||
-- means unlisted buffers (like from `nvim-tree`) don't affect decision.
|
||||
local listed_buffers = vim.tbl_filter(
|
||||
function(buf_id) return vim.fn.buflisted(buf_id) == 1 end,
|
||||
vim.api.nvim_list_bufs()
|
||||
)
|
||||
if #listed_buffers > 1 then return true end
|
||||
|
||||
-- - Current buffer is meant to show something else
|
||||
if vim.bo.filetype ~= '' then return true end
|
||||
|
||||
-- - Current buffer has any lines (something opened explicitly).
|
||||
-- NOTE: Usage of `line2byte(line('$') + 1) < 0` seemed to be fine, but it
|
||||
-- doesn't work if some automated changed was made to buffer while leaving it
|
||||
-- empty (returns 2 instead of -1). This was also the reason of not being
|
||||
-- able to test with child Neovim process from 'tests/helpers'.
|
||||
local n_lines = vim.api.nvim_buf_line_count(0)
|
||||
if n_lines > 1 then return true end
|
||||
local first_line = vim.api.nvim_buf_get_lines(0, 0, 1, true)[1]
|
||||
if string.len(first_line) > 0 then return true end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
H.possibly_execute = function(f, ...)
|
||||
if f == nil then return end
|
||||
return f(...)
|
||||
end
|
||||
|
||||
return MiniSessions
|
||||
1127
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/splitjoin.lua
Normal file
1127
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/splitjoin.lua
Normal file
File diff suppressed because it is too large
Load Diff
1594
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/starter.lua
Normal file
1594
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/starter.lua
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,695 @@
|
||||
--- *mini.statusline* Statusline
|
||||
--- *MiniStatusline*
|
||||
---
|
||||
--- MIT License Copyright (c) 2021 Evgeni Chasnovski
|
||||
---
|
||||
--- ==============================================================================
|
||||
---
|
||||
--- Features:
|
||||
--- - Define own custom statusline structure for active and inactive windows.
|
||||
--- This is done with a function which should return string appropriate for
|
||||
--- |statusline|. Its code should be similar to default one with structure:
|
||||
--- - Compute string data for every section you want to be displayed.
|
||||
--- - Combine them in groups with |MiniStatusline.combine_groups()|.
|
||||
---
|
||||
--- - Built-in active mode indicator with colors.
|
||||
---
|
||||
--- - Sections can hide information when window is too narrow (specific window
|
||||
--- width is configurable per section).
|
||||
---
|
||||
--- # Dependencies ~
|
||||
---
|
||||
--- Suggested dependencies (provide extra functionality, will work without them):
|
||||
---
|
||||
--- - Nerd font (to support extra icons).
|
||||
---
|
||||
--- - Enabled |MiniIcons| module for |MiniStatusline.section_fileinfo()|.
|
||||
--- Falls back to using 'nvim-tree/nvim-web-devicons' plugin or shows nothing.
|
||||
---
|
||||
--- - Enabled |MiniGit| module for |MiniStatusline.section_git()|.
|
||||
--- Falls back to using 'lewis6991/gitsigns.nvim' plugin or shows nothing.
|
||||
---
|
||||
--- - Enabled |MiniDiff| module for |MiniStatusline.section_diff()|.
|
||||
--- Falls back to using 'lewis6991/gitsigns.nvim' plugin or shows nothing.
|
||||
---
|
||||
--- # Setup ~
|
||||
---
|
||||
--- This module needs a setup with `require('mini.statusline').setup({})`
|
||||
--- (replace `{}` with your `config` table). It will create global Lua table
|
||||
--- `MiniStatusline` which you can use for scripting or manually (with
|
||||
--- `:lua MiniStatusline.*`).
|
||||
---
|
||||
--- See |MiniStatusline.config| for `config` structure and default values. For
|
||||
--- some content examples, see |MiniStatusline-example-content|.
|
||||
---
|
||||
--- You can override runtime config settings locally to buffer inside
|
||||
--- `vim.b.ministatusline_config` which should have same structure as
|
||||
--- `MiniStatusline.config`. See |mini.nvim-buffer-local-config| for more details.
|
||||
---
|
||||
--- # Highlight groups ~
|
||||
---
|
||||
--- Highlight depending on mode (second output from |MiniStatusline.section_mode|):
|
||||
--- * `MiniStatuslineModeNormal` - Normal mode.
|
||||
--- * `MiniStatuslineModeInsert` - Insert mode.
|
||||
--- * `MiniStatuslineModeVisual` - Visual mode.
|
||||
--- * `MiniStatuslineModeReplace` - Replace mode.
|
||||
--- * `MiniStatuslineModeCommand` - Command mode.
|
||||
--- * `MiniStatuslineModeOther` - other modes (like Terminal, etc.).
|
||||
---
|
||||
--- Highlight used in default statusline:
|
||||
--- * `MiniStatuslineDevinfo` - for "dev info" group
|
||||
--- (|MiniStatusline.section_git| and |MiniStatusline.section_diagnostics|).
|
||||
--- * `MiniStatuslineFilename` - for |MiniStatusline.section_filename| section.
|
||||
--- * `MiniStatuslineFileinfo` - for |MiniStatusline.section_fileinfo| section.
|
||||
---
|
||||
--- Other groups:
|
||||
--- * `MiniStatuslineInactive` - highliting in not focused window.
|
||||
---
|
||||
--- To change any highlight group, modify it directly with |:highlight|.
|
||||
---
|
||||
--- # Disabling ~
|
||||
---
|
||||
--- To disable (show empty statusline), set `vim.g.ministatusline_disable`
|
||||
--- (globally) or `vim.b.ministatusline_disable` (for a buffer) to `true`.
|
||||
--- Considering high number of different scenarios and customization
|
||||
--- intentions, writing exact rules for disabling module's functionality is
|
||||
--- left to user. See |mini.nvim-disabling-recipes| for common recipes.
|
||||
|
||||
--- Example content
|
||||
---
|
||||
--- # Default content ~
|
||||
---
|
||||
--- This function is used as default value for active content: >lua
|
||||
---
|
||||
--- function()
|
||||
--- local mode, mode_hl = MiniStatusline.section_mode({ trunc_width = 120 })
|
||||
--- local git = MiniStatusline.section_git({ trunc_width = 40 })
|
||||
--- local diff = MiniStatusline.section_diff({ trunc_width = 75 })
|
||||
--- local diagnostics = MiniStatusline.section_diagnostics({ trunc_width = 75 })
|
||||
--- local lsp = MiniStatusline.section_lsp({ trunc_width = 75 })
|
||||
--- local filename = MiniStatusline.section_filename({ trunc_width = 140 })
|
||||
--- local fileinfo = MiniStatusline.section_fileinfo({ trunc_width = 120 })
|
||||
--- local location = MiniStatusline.section_location({ trunc_width = 75 })
|
||||
--- local search = MiniStatusline.section_searchcount({ trunc_width = 75 })
|
||||
---
|
||||
--- return MiniStatusline.combine_groups({
|
||||
--- { hl = mode_hl, strings = { mode } },
|
||||
--- { hl = 'MiniStatuslineDevinfo', strings = { git, diff, diagnostics, lsp } },
|
||||
--- '%<', -- Mark general truncate point
|
||||
--- { hl = 'MiniStatuslineFilename', strings = { filename } },
|
||||
--- '%=', -- End left alignment
|
||||
--- { hl = 'MiniStatuslineFileinfo', strings = { fileinfo } },
|
||||
--- { hl = mode_hl, strings = { search, location } },
|
||||
--- })
|
||||
--- end
|
||||
--- <
|
||||
--- # Show boolean options ~
|
||||
---
|
||||
--- To compute section string for boolean option use variation of this code
|
||||
--- snippet inside content function (you can modify option itself, truncation
|
||||
--- width, short and long displayed names): >lua
|
||||
---
|
||||
--- local spell = vim.wo.spell and (MiniStatusline.is_truncated(120) and 'S' or 'SPELL') or ''
|
||||
--- <
|
||||
--- Here `x and y or z` is a common Lua way of doing ternary operator: if `x`
|
||||
--- is `true`-ish then return `y`, if not - return `z`.
|
||||
---@tag MiniStatusline-example-content
|
||||
|
||||
---@alias __statusline_args table Section arguments.
|
||||
---@alias __statusline_section string Section string.
|
||||
|
||||
-- Module definition ==========================================================
|
||||
local MiniStatusline = {}
|
||||
local H = {}
|
||||
|
||||
--- Module setup
|
||||
---
|
||||
---@param config table|nil Module config table. See |MiniStatusline.config|.
|
||||
---
|
||||
---@usage >lua
|
||||
--- require('mini.statusline').setup() -- use default config
|
||||
--- -- OR
|
||||
--- require('mini.statusline').setup({}) -- replace {} with your config table
|
||||
--- <
|
||||
MiniStatusline.setup = function(config)
|
||||
-- Export module
|
||||
_G.MiniStatusline = MiniStatusline
|
||||
|
||||
-- Setup config
|
||||
config = H.setup_config(config)
|
||||
|
||||
-- Apply config
|
||||
H.apply_config(config)
|
||||
|
||||
-- Define behavior
|
||||
H.create_autocommands()
|
||||
|
||||
-- - Disable built-in statusline in Quickfix window
|
||||
vim.g.qf_disable_statusline = 1
|
||||
|
||||
-- Create default highlighting
|
||||
H.create_default_hl()
|
||||
end
|
||||
|
||||
--- Module config
|
||||
---
|
||||
--- Default values:
|
||||
---@eval return MiniDoc.afterlines_to_code(MiniDoc.current.eval_section)
|
||||
MiniStatusline.config = {
|
||||
-- Content of statusline as functions which return statusline string. See
|
||||
-- `:h statusline` and code of default contents (used instead of `nil`).
|
||||
content = {
|
||||
-- Content for active window
|
||||
active = nil,
|
||||
-- Content for inactive window(s)
|
||||
inactive = nil,
|
||||
},
|
||||
|
||||
-- Whether to use icons by default
|
||||
use_icons = true,
|
||||
|
||||
-- Whether to set Vim's settings for statusline (make it always shown with
|
||||
-- 'laststatus' set to 2).
|
||||
-- To use global statusline, set this to `false` and 'laststatus' to 3.
|
||||
set_vim_settings = true,
|
||||
}
|
||||
--minidoc_afterlines_end
|
||||
|
||||
-- Module functionality =======================================================
|
||||
--- Compute content for active window
|
||||
MiniStatusline.active = function()
|
||||
if H.is_disabled() then return '' end
|
||||
|
||||
return (H.get_config().content.active or H.default_content_active)()
|
||||
end
|
||||
|
||||
--- Compute content for inactive window
|
||||
MiniStatusline.inactive = function()
|
||||
if H.is_disabled() then return '' end
|
||||
|
||||
return (H.get_config().content.inactive or H.default_content_inactive)()
|
||||
end
|
||||
|
||||
--- Combine groups of sections
|
||||
---
|
||||
--- Each group can be either a string or a table with fields `hl` (group's
|
||||
--- highlight group) and `strings` (strings representing sections).
|
||||
---
|
||||
--- General idea of this function is as follows;
|
||||
--- - String group is used as is (useful for special strings like `%<` or `%=`).
|
||||
--- - Each table group has own highlighting in `hl` field (if missing, the
|
||||
--- previous one is used) and string parts in `strings` field. Non-empty
|
||||
--- strings from `strings` are separated by one space. Non-empty groups are
|
||||
--- separated by two spaces (one for each highlighting).
|
||||
---
|
||||
---@param groups table Array of groups.
|
||||
---
|
||||
---@return string String suitable for 'statusline'.
|
||||
MiniStatusline.combine_groups = function(groups)
|
||||
local parts = vim.tbl_map(function(s)
|
||||
if type(s) == 'string' then return s end
|
||||
if type(s) ~= 'table' then return '' end
|
||||
|
||||
local string_arr = vim.tbl_filter(function(x) return type(x) == 'string' and x ~= '' end, s.strings or {})
|
||||
local str = table.concat(string_arr, ' ')
|
||||
|
||||
-- Use previous highlight group
|
||||
if s.hl == nil then return ' ' .. str .. ' ' end
|
||||
|
||||
-- Allow using this highlight group later
|
||||
if str:len() == 0 then return '%#' .. s.hl .. '#' end
|
||||
|
||||
return string.format('%%#%s# %s ', s.hl, str)
|
||||
end, groups)
|
||||
|
||||
return table.concat(parts, '')
|
||||
end
|
||||
|
||||
--- Decide whether to truncate
|
||||
---
|
||||
--- This basically computes window width and compares it to `trunc_width`: if
|
||||
--- window is smaller then truncate; otherwise don't. Don't truncate by
|
||||
--- default.
|
||||
---
|
||||
--- Use this to manually decide if section needs truncation or not.
|
||||
---
|
||||
---@param trunc_width number|nil Truncation width. If `nil`, output is `false`.
|
||||
---
|
||||
---@return boolean Whether to truncate.
|
||||
MiniStatusline.is_truncated = function(trunc_width)
|
||||
-- Use -1 to default to 'not truncated'
|
||||
local cur_width = vim.o.laststatus == 3 and vim.o.columns or vim.api.nvim_win_get_width(0)
|
||||
return cur_width < (trunc_width or -1)
|
||||
end
|
||||
|
||||
-- Sections ===================================================================
|
||||
-- Functions should return output text without whitespace on sides.
|
||||
-- Return empty string to omit section.
|
||||
|
||||
--- Section for Vim |mode()|
|
||||
---
|
||||
--- Short output is returned if window width is lower than `args.trunc_width`.
|
||||
---
|
||||
---@param args __statusline_args
|
||||
---
|
||||
---@return ... Section string and mode's highlight group.
|
||||
MiniStatusline.section_mode = function(args)
|
||||
local mode_info = H.modes[vim.fn.mode()]
|
||||
|
||||
local mode = MiniStatusline.is_truncated(args.trunc_width) and mode_info.short or mode_info.long
|
||||
|
||||
return mode, mode_info.hl
|
||||
end
|
||||
|
||||
--- Section for Git information
|
||||
---
|
||||
--- Shows Git summary from |MiniGit| (should be set up; recommended). To tweak
|
||||
--- formatting of what data is shown, modify buffer-local summary string directly
|
||||
--- as described in |MiniGit-examples|.
|
||||
---
|
||||
--- If 'mini.git' is not set up, section falls back on 'lewis6991/gitsigns' data
|
||||
--- or showing empty string.
|
||||
---
|
||||
--- Empty string is returned if window width is lower than `args.trunc_width`.
|
||||
---
|
||||
---@param args __statusline_args Use `args.icon` to supply your own icon.
|
||||
---
|
||||
---@return __statusline_section
|
||||
MiniStatusline.section_git = function(args)
|
||||
if MiniStatusline.is_truncated(args.trunc_width) then return '' end
|
||||
|
||||
local summary = vim.b.minigit_summary_string or vim.b.gitsigns_head
|
||||
if summary == nil then return '' end
|
||||
|
||||
local use_icons = H.use_icons or H.get_config().use_icons
|
||||
local icon = args.icon or (use_icons and '' or 'Git')
|
||||
return icon .. ' ' .. (summary == '' and '-' or summary)
|
||||
end
|
||||
|
||||
--- Section for diff information
|
||||
---
|
||||
--- Shows diff summary from |MiniDiff| (should be set up; recommended). To tweak
|
||||
--- formatting of what data is shown, modify buffer-local summary string directly
|
||||
--- as described in |MiniDiff-diff-summary|.
|
||||
---
|
||||
--- If 'mini.diff' is not set up, section falls back on 'lewis6991/gitsigns' data
|
||||
--- or showing empty string.
|
||||
---
|
||||
--- Empty string is returned if window width is lower than `args.trunc_width`.
|
||||
---
|
||||
---@param args __statusline_args Use `args.icon` to supply your own icon.
|
||||
---
|
||||
---@return __statusline_section
|
||||
MiniStatusline.section_diff = function(args)
|
||||
if MiniStatusline.is_truncated(args.trunc_width) then return '' end
|
||||
|
||||
local summary = vim.b.minidiff_summary_string or vim.b.gitsigns_status
|
||||
if summary == nil then return '' end
|
||||
|
||||
local use_icons = H.use_icons or H.get_config().use_icons
|
||||
local icon = args.icon or (use_icons and '' or 'Diff')
|
||||
return icon .. ' ' .. (summary == '' and '-' or summary)
|
||||
end
|
||||
|
||||
--- Section for Neovim's builtin diagnostics
|
||||
---
|
||||
--- Shows nothing if diagnostics is disabled, no diagnostic is set, or for short
|
||||
--- output. Otherwise uses |vim.diagnostic.get()| to compute and show number of
|
||||
--- errors ('E'), warnings ('W'), information ('I'), and hints ('H').
|
||||
---
|
||||
--- Short output is returned if window width is lower than `args.trunc_width`.
|
||||
---
|
||||
---@param args __statusline_args Use `args.icon` to supply your own icon.
|
||||
--- Use `args.signs` to use custom signs per severity level name. For example: >lua
|
||||
---
|
||||
--- { ERROR = '!', WARN = '?', INFO = '@', HINT = '*' }
|
||||
--- <
|
||||
---@return __statusline_section
|
||||
MiniStatusline.section_diagnostics = function(args)
|
||||
if MiniStatusline.is_truncated(args.trunc_width) or H.diagnostic_is_disabled() then return '' end
|
||||
|
||||
-- Construct string parts
|
||||
local count = H.diagnostic_get_count()
|
||||
local severity, signs, t = vim.diagnostic.severity, args.signs or {}, {}
|
||||
for _, level in ipairs(H.diagnostic_levels) do
|
||||
local n = count[severity[level.name]] or 0
|
||||
-- Add level info only if diagnostic is present
|
||||
if n > 0 then table.insert(t, ' ' .. (signs[level.name] or level.sign) .. n) end
|
||||
end
|
||||
if #t == 0 then return '' end
|
||||
|
||||
local use_icons = H.use_icons or H.get_config().use_icons
|
||||
local icon = args.icon or (use_icons and '' or 'Diag')
|
||||
return icon .. table.concat(t, '')
|
||||
end
|
||||
|
||||
--- Section for attached LSP servers
|
||||
---
|
||||
--- Shows number of LSP servers (each as separate "+" character) attached to
|
||||
--- current buffer or nothing if none is attached.
|
||||
--- Nothing is shown if window width is lower than `args.trunc_width`.
|
||||
---
|
||||
---@param args __statusline_args Use `args.icon` to supply your own icon.
|
||||
---
|
||||
---@return __statusline_section
|
||||
MiniStatusline.section_lsp = function(args)
|
||||
if MiniStatusline.is_truncated(args.trunc_width) then return '' end
|
||||
|
||||
local attached = H.get_attached_lsp()
|
||||
if attached == '' then return '' end
|
||||
|
||||
local use_icons = H.use_icons or H.get_config().use_icons
|
||||
local icon = args.icon or (use_icons and '' or 'LSP')
|
||||
return icon .. ' ' .. attached
|
||||
end
|
||||
|
||||
--- Section for file name
|
||||
---
|
||||
--- Show full file name or relative in short output.
|
||||
---
|
||||
--- Short output is returned if window width is lower than `args.trunc_width`.
|
||||
---
|
||||
---@param args __statusline_args
|
||||
---
|
||||
---@return __statusline_section
|
||||
MiniStatusline.section_filename = function(args)
|
||||
-- In terminal always use plain name
|
||||
if vim.bo.buftype == 'terminal' then
|
||||
return '%t'
|
||||
elseif MiniStatusline.is_truncated(args.trunc_width) then
|
||||
-- File name with 'truncate', 'modified', 'readonly' flags
|
||||
-- Use relative path if truncated
|
||||
return '%f%m%r'
|
||||
else
|
||||
-- Use fullpath if not truncated
|
||||
return '%F%m%r'
|
||||
end
|
||||
end
|
||||
|
||||
--- Section for file information
|
||||
---
|
||||
--- Short output contains only buffer's 'filetype' and is returned if window
|
||||
--- width is lower than `args.trunc_width` or buffer is not normal.
|
||||
---
|
||||
--- Nothing is shown if there is no 'filetype' set (treated as temporary buffer).
|
||||
---
|
||||
--- If `config.use_icons` is true and icon provider is present (see
|
||||
--- "Dependencies" section in |mini.statusline|), shows icon near the filetype.
|
||||
---
|
||||
---@param args __statusline_args
|
||||
---
|
||||
---@return __statusline_section
|
||||
MiniStatusline.section_fileinfo = function(args)
|
||||
local filetype = vim.bo.filetype
|
||||
|
||||
-- Don't show anything if there is no filetype
|
||||
if filetype == '' then return '' end
|
||||
|
||||
-- Add filetype icon
|
||||
H.ensure_get_icon()
|
||||
if H.get_icon ~= nil then filetype = H.get_icon(filetype) .. ' ' .. filetype end
|
||||
|
||||
-- Construct output string if truncated or buffer is not normal
|
||||
if MiniStatusline.is_truncated(args.trunc_width) or vim.bo.buftype ~= '' then return filetype end
|
||||
|
||||
-- Construct output string with extra file info
|
||||
local encoding = vim.bo.fileencoding or vim.bo.encoding
|
||||
local format = vim.bo.fileformat
|
||||
local size = H.get_filesize()
|
||||
|
||||
return string.format('%s %s[%s] %s', filetype, encoding, format, size)
|
||||
end
|
||||
|
||||
--- Section for location inside buffer
|
||||
---
|
||||
--- Show location inside buffer in the form:
|
||||
--- - Normal: `'<cursor line>|<total lines>│<cursor column>|<total columns>'`
|
||||
--- - Short: `'<cursor line>│<cursor column>'`
|
||||
---
|
||||
--- Short output is returned if window width is lower than `args.trunc_width`.
|
||||
---
|
||||
---@param args __statusline_args
|
||||
---
|
||||
---@return __statusline_section
|
||||
MiniStatusline.section_location = function(args)
|
||||
-- Use virtual column number to allow update when past last column
|
||||
if MiniStatusline.is_truncated(args.trunc_width) then return '%l│%2v' end
|
||||
|
||||
-- Use `virtcol()` to correctly handle multi-byte characters
|
||||
return '%l|%L│%2v|%-2{virtcol("$") - 1}'
|
||||
end
|
||||
|
||||
--- Section for current search count
|
||||
---
|
||||
--- Show the current status of |searchcount()|. Empty output is returned if
|
||||
--- window width is lower than `args.trunc_width`, search highlighting is not
|
||||
--- on (see |v:hlsearch|), or if number of search result is 0.
|
||||
---
|
||||
--- `args.options` is forwarded to |searchcount()|. By default it recomputes
|
||||
--- data on every call which can be computationally expensive (although still
|
||||
--- usually on 0.1 ms order of magnitude). To prevent this, supply
|
||||
--- `args.options = { recompute = false }`.
|
||||
---
|
||||
---@param args __statusline_args
|
||||
---
|
||||
---@return __statusline_section
|
||||
MiniStatusline.section_searchcount = function(args)
|
||||
if vim.v.hlsearch == 0 or MiniStatusline.is_truncated(args.trunc_width) then return '' end
|
||||
-- `searchcount()` can return errors because it is evaluated very often in
|
||||
-- statusline. For example, when typing `/` followed by `\(`, it gives E54.
|
||||
local ok, s_count = pcall(vim.fn.searchcount, (args or {}).options or { recompute = true })
|
||||
if not ok or s_count.current == nil or s_count.total == 0 then return '' end
|
||||
|
||||
if s_count.incomplete == 1 then return '?/?' end
|
||||
|
||||
local too_many = '>' .. s_count.maxcount
|
||||
local current = s_count.current > s_count.maxcount and too_many or s_count.current
|
||||
local total = s_count.total > s_count.maxcount and too_many or s_count.total
|
||||
return current .. '/' .. total
|
||||
end
|
||||
|
||||
-- Helper data ================================================================
|
||||
-- Module default config
|
||||
H.default_config = vim.deepcopy(MiniStatusline.config)
|
||||
|
||||
-- Showed diagnostic levels
|
||||
H.diagnostic_levels = {
|
||||
{ name = 'ERROR', sign = 'E' },
|
||||
{ name = 'WARN', sign = 'W' },
|
||||
{ name = 'INFO', sign = 'I' },
|
||||
{ name = 'HINT', sign = 'H' },
|
||||
}
|
||||
|
||||
-- String representation of attached LSP clients per buffer id
|
||||
H.attached_lsp = {}
|
||||
|
||||
-- Helper functionality =======================================================
|
||||
-- Settings -------------------------------------------------------------------
|
||||
H.setup_config = function(config)
|
||||
-- General idea: if some table elements are not present in user-supplied
|
||||
-- `config`, take them from default config
|
||||
vim.validate({ config = { config, 'table', true } })
|
||||
config = vim.tbl_deep_extend('force', vim.deepcopy(H.default_config), config or {})
|
||||
|
||||
-- Validate per nesting level to produce correct error message
|
||||
vim.validate({
|
||||
content = { config.content, 'table' },
|
||||
set_vim_settings = { config.set_vim_settings, 'boolean' },
|
||||
use_icons = { config.use_icons, 'boolean' },
|
||||
})
|
||||
|
||||
vim.validate({
|
||||
['content.active'] = { config.content.active, 'function', true },
|
||||
['content.inactive'] = { config.content.inactive, 'function', true },
|
||||
})
|
||||
|
||||
return config
|
||||
end
|
||||
|
||||
H.apply_config = function(config)
|
||||
MiniStatusline.config = config
|
||||
|
||||
-- Set settings to ensure statusline is displayed properly
|
||||
if config.set_vim_settings then vim.o.laststatus = 2 end
|
||||
|
||||
-- Ensure proper 'statusline' values (to not rely on autocommands trigger)
|
||||
H.ensure_content()
|
||||
|
||||
-- Set global value to reduce flickering when first time entering buffer, as
|
||||
-- it is used by default before content is ensured on next loop
|
||||
vim.go.statusline = '%{%v:lua.MiniStatusline.active()%}'
|
||||
end
|
||||
|
||||
H.create_autocommands = function()
|
||||
local augroup = vim.api.nvim_create_augroup('MiniStatusline', {})
|
||||
|
||||
local au = function(event, pattern, callback, desc)
|
||||
vim.api.nvim_create_autocmd(event, { group = augroup, pattern = pattern, callback = callback, desc = desc })
|
||||
end
|
||||
|
||||
au({ 'WinEnter', 'BufWinEnter' }, '*', H.ensure_content, 'Ensure statusline content')
|
||||
|
||||
-- Use `schedule_wrap()` because at `LspDetach` server is still present
|
||||
local track_lsp = vim.schedule_wrap(function(data)
|
||||
H.attached_lsp[data.buf] = H.compute_attached_lsp(data.buf)
|
||||
vim.cmd('redrawstatus')
|
||||
end)
|
||||
au({ 'LspAttach', 'LspDetach' }, '*', track_lsp, 'Track LSP clients')
|
||||
end
|
||||
|
||||
--stylua: ignore
|
||||
H.create_default_hl = function()
|
||||
local set_default_hl = function(name, data)
|
||||
data.default = true
|
||||
vim.api.nvim_set_hl(0, name, data)
|
||||
end
|
||||
|
||||
set_default_hl('MiniStatuslineModeNormal', { link = 'Cursor' })
|
||||
set_default_hl('MiniStatuslineModeInsert', { link = 'DiffChange' })
|
||||
set_default_hl('MiniStatuslineModeVisual', { link = 'DiffAdd' })
|
||||
set_default_hl('MiniStatuslineModeReplace', { link = 'DiffDelete' })
|
||||
set_default_hl('MiniStatuslineModeCommand', { link = 'DiffText' })
|
||||
set_default_hl('MiniStatuslineModeOther', { link = 'IncSearch' })
|
||||
|
||||
set_default_hl('MiniStatuslineDevinfo', { link = 'StatusLine' })
|
||||
set_default_hl('MiniStatuslineFilename', { link = 'StatusLineNC' })
|
||||
set_default_hl('MiniStatuslineFileinfo', { link = 'StatusLine' })
|
||||
set_default_hl('MiniStatuslineInactive', { link = 'StatusLineNC' })
|
||||
end
|
||||
|
||||
H.is_disabled = function() return vim.g.ministatusline_disable == true or vim.b.ministatusline_disable == true end
|
||||
|
||||
H.get_config = function(config)
|
||||
return vim.tbl_deep_extend('force', MiniStatusline.config, vim.b.ministatusline_config or {}, config or {})
|
||||
end
|
||||
|
||||
-- Content --------------------------------------------------------------------
|
||||
H.ensure_content = vim.schedule_wrap(function()
|
||||
-- NOTE: Use `schedule_wrap()` to properly work inside autocommands because
|
||||
-- they might temporarily change current window
|
||||
local cur_win_id, is_global_stl = vim.api.nvim_get_current_win(), vim.o.laststatus == 3
|
||||
for _, win_id in ipairs(vim.api.nvim_list_wins()) do
|
||||
vim.wo[win_id].statusline = (win_id == cur_win_id or is_global_stl) and '%{%v:lua.MiniStatusline.active()%}'
|
||||
or '%{%v:lua.MiniStatusline.inactive()%}'
|
||||
end
|
||||
end)
|
||||
|
||||
-- Mode -----------------------------------------------------------------------
|
||||
-- Custom `^V` and `^S` symbols to make this file appropriate for copy-paste
|
||||
-- (otherwise those symbols are not displayed).
|
||||
local CTRL_S = vim.api.nvim_replace_termcodes('<C-S>', true, true, true)
|
||||
local CTRL_V = vim.api.nvim_replace_termcodes('<C-V>', true, true, true)
|
||||
|
||||
-- stylua: ignore start
|
||||
H.modes = setmetatable({
|
||||
['n'] = { long = 'Normal', short = 'N', hl = 'MiniStatuslineModeNormal' },
|
||||
['v'] = { long = 'Visual', short = 'V', hl = 'MiniStatuslineModeVisual' },
|
||||
['V'] = { long = 'V-Line', short = 'V-L', hl = 'MiniStatuslineModeVisual' },
|
||||
[CTRL_V] = { long = 'V-Block', short = 'V-B', hl = 'MiniStatuslineModeVisual' },
|
||||
['s'] = { long = 'Select', short = 'S', hl = 'MiniStatuslineModeVisual' },
|
||||
['S'] = { long = 'S-Line', short = 'S-L', hl = 'MiniStatuslineModeVisual' },
|
||||
[CTRL_S] = { long = 'S-Block', short = 'S-B', hl = 'MiniStatuslineModeVisual' },
|
||||
['i'] = { long = 'Insert', short = 'I', hl = 'MiniStatuslineModeInsert' },
|
||||
['R'] = { long = 'Replace', short = 'R', hl = 'MiniStatuslineModeReplace' },
|
||||
['c'] = { long = 'Command', short = 'C', hl = 'MiniStatuslineModeCommand' },
|
||||
['r'] = { long = 'Prompt', short = 'P', hl = 'MiniStatuslineModeOther' },
|
||||
['!'] = { long = 'Shell', short = 'Sh', hl = 'MiniStatuslineModeOther' },
|
||||
['t'] = { long = 'Terminal', short = 'T', hl = 'MiniStatuslineModeOther' },
|
||||
}, {
|
||||
-- By default return 'Unknown' but this shouldn't be needed
|
||||
__index = function()
|
||||
return { long = 'Unknown', short = 'U', hl = '%#MiniStatuslineModeOther#' }
|
||||
end,
|
||||
})
|
||||
-- stylua: ignore end
|
||||
|
||||
-- Default content ------------------------------------------------------------
|
||||
--stylua: ignore
|
||||
H.default_content_active = function()
|
||||
H.use_icons = H.get_config().use_icons
|
||||
local mode, mode_hl = MiniStatusline.section_mode({ trunc_width = 120 })
|
||||
local git = MiniStatusline.section_git({ trunc_width = 40 })
|
||||
local diff = MiniStatusline.section_diff({ trunc_width = 75 })
|
||||
local diagnostics = MiniStatusline.section_diagnostics({ trunc_width = 75 })
|
||||
local lsp = MiniStatusline.section_lsp({ trunc_width = 75 })
|
||||
local filename = MiniStatusline.section_filename({ trunc_width = 140 })
|
||||
local fileinfo = MiniStatusline.section_fileinfo({ trunc_width = 120 })
|
||||
local location = MiniStatusline.section_location({ trunc_width = 75 })
|
||||
local search = MiniStatusline.section_searchcount({ trunc_width = 75 })
|
||||
H.use_icons = nil
|
||||
|
||||
-- Usage of `MiniStatusline.combine_groups()` ensures highlighting and
|
||||
-- correct padding with spaces between groups (accounts for 'missing'
|
||||
-- sections, etc.)
|
||||
return MiniStatusline.combine_groups({
|
||||
{ hl = mode_hl, strings = { mode } },
|
||||
{ hl = 'MiniStatuslineDevinfo', strings = { git, diff, diagnostics, lsp } },
|
||||
'%<', -- Mark general truncate point
|
||||
{ hl = 'MiniStatuslineFilename', strings = { filename } },
|
||||
'%=', -- End left alignment
|
||||
{ hl = 'MiniStatuslineFileinfo', strings = { fileinfo } },
|
||||
{ hl = mode_hl, strings = { search, location } },
|
||||
})
|
||||
end
|
||||
|
||||
H.default_content_inactive = function() return '%#MiniStatuslineInactive#%F%=' end
|
||||
|
||||
-- LSP ------------------------------------------------------------------------
|
||||
H.get_attached_lsp = function() return H.attached_lsp[vim.api.nvim_get_current_buf()] or '' end
|
||||
|
||||
H.compute_attached_lsp = function(buf_id) return string.rep('+', vim.tbl_count(H.get_buf_lsp_clients(buf_id))) end
|
||||
|
||||
H.get_buf_lsp_clients = function(buf_id) return vim.lsp.get_clients({ bufnr = buf_id }) end
|
||||
if vim.fn.has('nvim-0.10') == 0 then
|
||||
H.get_buf_lsp_clients = function(buf_id) return vim.lsp.buf_get_clients(buf_id) end
|
||||
end
|
||||
|
||||
-- Diagnostics ----------------------------------------------------------------
|
||||
H.diagnostic_get_count = function()
|
||||
local res = {}
|
||||
for _, d in ipairs(vim.diagnostic.get(0)) do
|
||||
res[d.severity] = (res[d.severity] or 0) + 1
|
||||
end
|
||||
return res
|
||||
end
|
||||
if vim.fn.has('nvim-0.10') == 1 then H.diagnostic_get_count = function() return vim.diagnostic.count(0) end end
|
||||
|
||||
if vim.fn.has('nvim-0.10') == 1 then
|
||||
H.diagnostic_is_disabled = function(_) return not vim.diagnostic.is_enabled({ bufnr = 0 }) end
|
||||
elseif vim.fn.has('nvim-0.9') == 1 then
|
||||
H.diagnostic_is_disabled = function(_) return vim.diagnostic.is_disabled(0) end
|
||||
else
|
||||
H.diagnostic_is_disabled = function(_) return false end
|
||||
end
|
||||
|
||||
-- Utilities ------------------------------------------------------------------
|
||||
H.get_filesize = function()
|
||||
local size = vim.fn.getfsize(vim.fn.getreg('%'))
|
||||
if size < 1024 then
|
||||
return string.format('%dB', size)
|
||||
elseif size < 1048576 then
|
||||
return string.format('%.2fKiB', size / 1024)
|
||||
else
|
||||
return string.format('%.2fMiB', size / 1048576)
|
||||
end
|
||||
end
|
||||
|
||||
H.ensure_get_icon = function()
|
||||
if not (H.use_icons or H.get_config().use_icons) then
|
||||
-- Show no icon
|
||||
H.get_icon = nil
|
||||
elseif H.get_icon ~= nil then
|
||||
-- Cache only once
|
||||
return
|
||||
elseif _G.MiniIcons ~= nil then
|
||||
-- Prefer 'mini.icons'
|
||||
H.get_icon = function(filetype) return (_G.MiniIcons.get('filetype', filetype)) end
|
||||
else
|
||||
-- Try falling back to 'nvim-web-devicons'
|
||||
local has_devicons, devicons = pcall(require, 'nvim-web-devicons')
|
||||
if not has_devicons then return end
|
||||
H.get_icon = function() return (devicons.get_icon(vim.fn.expand('%:t'), nil, { default = true })) end
|
||||
end
|
||||
end
|
||||
|
||||
return MiniStatusline
|
||||
2280
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/surround.lua
Normal file
2280
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/surround.lua
Normal file
File diff suppressed because it is too large
Load Diff
550
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/tabline.lua
Normal file
550
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/tabline.lua
Normal file
@ -0,0 +1,550 @@
|
||||
--- *mini.tabline* Tabline
|
||||
--- *MiniTabline*
|
||||
---
|
||||
--- MIT License Copyright (c) 2021 Evgeni Chasnovski
|
||||
---
|
||||
--- ==============================================================================
|
||||
---
|
||||
--- Key idea: show all listed buffers in readable way with minimal total width.
|
||||
--- Also allow showing extra information section in case of multiple vim tabpages.
|
||||
---
|
||||
--- Features:
|
||||
--- - Buffers are listed in the order of their identifier (see |bufnr()|).
|
||||
---
|
||||
--- - Different highlight groups for "states" of buffer affecting 'buffer tabs'.
|
||||
---
|
||||
--- - Buffer names are made unique by extending paths to files or appending
|
||||
--- unique identifier to buffers without name.
|
||||
---
|
||||
--- - Current buffer is displayed "optimally centered" (in center of screen
|
||||
--- while maximizing the total number of buffers shown) when there are many
|
||||
--- buffers open.
|
||||
---
|
||||
--- - 'Buffer tabs' are clickable if Neovim allows it.
|
||||
---
|
||||
--- What it doesn't do:
|
||||
--- - Custom buffer order is not supported.
|
||||
---
|
||||
--- # Dependencies ~
|
||||
---
|
||||
--- Suggested dependencies (provide extra functionality, will work without them):
|
||||
---
|
||||
--- - Enabled |MiniIcons| module to show icons near file names.
|
||||
--- Falls back to using 'nvim-tree/nvim-web-devicons' plugin or shows nothing.
|
||||
---
|
||||
--- # Setup ~
|
||||
---
|
||||
--- This module needs a setup with `require('mini.tabline').setup({})`
|
||||
--- (replace `{}` with your `config` table). It will create global Lua table
|
||||
--- `MiniTabline` which you can use for scripting or manually (with
|
||||
--- `:lua MiniTabline.*`).
|
||||
---
|
||||
--- See |MiniTabline.config| for `config` structure and default values.
|
||||
---
|
||||
--- You can override runtime config settings locally to buffer inside
|
||||
--- `vim.b.minitabline_config` which should have same structure as
|
||||
--- `MiniTabline.config`. See |mini.nvim-buffer-local-config| for more details.
|
||||
---
|
||||
--- # Highlight groups ~
|
||||
---
|
||||
--- * `MiniTablineCurrent` - buffer is current (has cursor in it).
|
||||
--- * `MiniTablineVisible` - buffer is visible (displayed in some window).
|
||||
--- * `MiniTablineHidden` - buffer is hidden (not displayed).
|
||||
--- * `MiniTablineModifiedCurrent` - buffer is modified and current.
|
||||
--- * `MiniTablineModifiedVisible` - buffer is modified and visible.
|
||||
--- * `MiniTablineModifiedHidden` - buffer is modified and hidden.
|
||||
--- * `MiniTablineFill` - unused right space of tabline.
|
||||
--- * `MiniTablineTabpagesection` - section with tabpage information.
|
||||
---
|
||||
--- To change any highlight group, modify it directly with |:highlight|.
|
||||
---
|
||||
--- # Disabling ~
|
||||
---
|
||||
--- To disable (show empty tabline), set `vim.g.minitabline_disable` (globally) or
|
||||
--- `vim.b.minitabline_disable` (for a buffer) to `true`. Considering high number
|
||||
--- of different scenarios and customization intentions, writing exact rules
|
||||
--- for disabling module's functionality is left to user. See
|
||||
--- |mini.nvim-disabling-recipes| for common recipes.
|
||||
|
||||
-- Module definition ==========================================================
|
||||
local MiniTabline = {}
|
||||
local H = {}
|
||||
|
||||
--- Module setup
|
||||
---
|
||||
---@param config table|nil Module config table. See |MiniTabline.config|.
|
||||
---
|
||||
---@usage >lua
|
||||
--- require('mini.tabline').setup() -- use default config
|
||||
--- -- OR
|
||||
--- require('mini.tabline').setup({}) -- replace {} with your config table
|
||||
--- <
|
||||
MiniTabline.setup = function(config)
|
||||
-- Export module
|
||||
_G.MiniTabline = MiniTabline
|
||||
|
||||
-- Setup config
|
||||
config = H.setup_config(config)
|
||||
|
||||
-- Apply config
|
||||
H.apply_config(config)
|
||||
|
||||
-- Function to make tabs clickable
|
||||
vim.api.nvim_exec(
|
||||
[[function! MiniTablineSwitchBuffer(buf_id, clicks, button, mod)
|
||||
execute 'buffer' a:buf_id
|
||||
endfunction]],
|
||||
false
|
||||
)
|
||||
|
||||
-- Create default highlighting
|
||||
H.create_default_hl()
|
||||
end
|
||||
|
||||
--- Module config
|
||||
---
|
||||
--- Default values:
|
||||
---@eval return MiniDoc.afterlines_to_code(MiniDoc.current.eval_section)
|
||||
---@text # Format ~
|
||||
---
|
||||
--- `config.format` is a callable that takes buffer identifier and pre-computed
|
||||
--- label as arguments and returns a string with formatted label.
|
||||
--- This function will be called for all displayable in tabline buffers.
|
||||
--- Default: |MiniTabline.default_format()|.
|
||||
---
|
||||
--- Example of adding "+" suffix for modified buffers: >lua
|
||||
---
|
||||
--- function(buf_id, label)
|
||||
--- local suffix = vim.bo[buf_id].modified and '+ ' or ''
|
||||
--- return MiniTabline.default_format(buf_id, label) .. suffix
|
||||
--- end
|
||||
--- <
|
||||
MiniTabline.config = {
|
||||
-- Whether to show file icons (requires 'mini.icons')
|
||||
show_icons = true,
|
||||
|
||||
-- Function which formats the tab label
|
||||
-- By default surrounds with space and possibly prepends with icon
|
||||
format = nil,
|
||||
|
||||
-- Whether to set Vim's settings for tabline (make it always shown and
|
||||
-- allow hidden buffers)
|
||||
set_vim_settings = true,
|
||||
|
||||
-- Where to show tabpage section in case of multiple vim tabpages.
|
||||
-- One of 'left', 'right', 'none'.
|
||||
tabpage_section = 'left',
|
||||
}
|
||||
--minidoc_afterlines_end
|
||||
|
||||
-- Module functionality =======================================================
|
||||
--- Make string for |tabline|
|
||||
MiniTabline.make_tabline_string = function()
|
||||
if H.is_disabled() then return '' end
|
||||
|
||||
H.make_tabpage_section()
|
||||
H.list_tabs()
|
||||
H.finalize_labels()
|
||||
H.fit_width()
|
||||
|
||||
return H.concat_tabs()
|
||||
end
|
||||
|
||||
--- Default tab format
|
||||
---
|
||||
--- Used by default as `config.format`.
|
||||
--- Prepends label with padded icon based on buffer's name (if `show_icon`
|
||||
--- in |MiniTabline.config| is `true`) and surrounds label with single space.
|
||||
--- Note: it is meant to be used only as part of `format` in |MiniTabline.config|.
|
||||
---
|
||||
---@param buf_id number Buffer identifier.
|
||||
---@param label string Pre-computed label.
|
||||
---
|
||||
---@return string Formatted label.
|
||||
MiniTabline.default_format = function(buf_id, label)
|
||||
if H.get_icon == nil then return string.format(' %s ', label) end
|
||||
return string.format(' %s %s ', H.get_icon(vim.api.nvim_buf_get_name(buf_id)), label)
|
||||
end
|
||||
|
||||
-- Helper data ================================================================
|
||||
-- Module default config
|
||||
H.default_config = vim.deepcopy(MiniTabline.config)
|
||||
|
||||
-- Table to keep track of tabs
|
||||
H.tabs = {}
|
||||
|
||||
-- Indicator of whether there is clickable support
|
||||
H.tablineat = vim.fn.has('tablineat')
|
||||
|
||||
-- Keep track of initially unnamed buffers
|
||||
H.unnamed_buffers_seq_ids = {}
|
||||
|
||||
-- Separator of file path
|
||||
H.path_sep = package.config:sub(1, 1)
|
||||
|
||||
-- String with tabpage prefix
|
||||
H.tabpage_section = ''
|
||||
|
||||
-- Buffer number of center buffer
|
||||
H.center_buf_id = nil
|
||||
|
||||
-- Helper functionality =======================================================
|
||||
-- Settings -------------------------------------------------------------------
|
||||
H.setup_config = function(config)
|
||||
-- General idea: if some table elements are not present in user-supplied
|
||||
-- `config`, take them from default config
|
||||
vim.validate({ config = { config, 'table', true } })
|
||||
config = vim.tbl_deep_extend('force', vim.deepcopy(H.default_config), config or {})
|
||||
|
||||
vim.validate({
|
||||
show_icons = { config.show_icons, 'boolean' },
|
||||
format = { config.format, 'function', true },
|
||||
set_vim_settings = { config.set_vim_settings, 'boolean' },
|
||||
tabpage_section = { config.tabpage_section, 'string' },
|
||||
})
|
||||
|
||||
return config
|
||||
end
|
||||
|
||||
H.apply_config = function(config)
|
||||
MiniTabline.config = config
|
||||
|
||||
-- Set settings to ensure tabline is displayed properly
|
||||
if config.set_vim_settings then
|
||||
vim.o.showtabline = 2 -- Always show tabline
|
||||
vim.o.hidden = true -- Allow switching buffers without saving them
|
||||
end
|
||||
|
||||
-- Set tabline string
|
||||
vim.o.tabline = '%!v:lua.MiniTabline.make_tabline_string()'
|
||||
end
|
||||
|
||||
--stylua: ignore
|
||||
H.create_default_hl = function()
|
||||
local set_default_hl = function(name, data)
|
||||
data.default = true
|
||||
vim.api.nvim_set_hl(0, name, data)
|
||||
end
|
||||
|
||||
set_default_hl('MiniTablineCurrent', { link = 'TabLineSel' })
|
||||
set_default_hl('MiniTablineVisible', { link = 'TabLineSel' })
|
||||
set_default_hl('MiniTablineHidden', { link = 'TabLine' })
|
||||
|
||||
set_default_hl('MiniTablineModifiedCurrent', { link = 'StatusLine' })
|
||||
set_default_hl('MiniTablineModifiedVisible', { link = 'StatusLine' })
|
||||
set_default_hl('MiniTablineModifiedHidden', { link = 'StatusLineNC' })
|
||||
|
||||
set_default_hl('MiniTablineTabpagesection', { link = 'Search' })
|
||||
|
||||
set_default_hl('MiniTablineFill', { link = 'Normal' })
|
||||
end
|
||||
|
||||
H.is_disabled = function() return vim.g.minitabline_disable == true or vim.b.minitabline_disable == true end
|
||||
|
||||
H.get_config = function(config)
|
||||
return vim.tbl_deep_extend('force', MiniTabline.config, vim.b.minitabline_config or {}, config or {})
|
||||
end
|
||||
|
||||
-- Work with tabpages ---------------------------------------------------------
|
||||
H.make_tabpage_section = function()
|
||||
local n_tabpages = vim.fn.tabpagenr('$')
|
||||
if n_tabpages == 1 or H.get_config().tabpage_section == 'none' then
|
||||
H.tabpage_section = ''
|
||||
return
|
||||
end
|
||||
|
||||
local cur_tabpagenr = vim.fn.tabpagenr()
|
||||
H.tabpage_section = (' Tab %s/%s '):format(cur_tabpagenr, n_tabpages)
|
||||
end
|
||||
|
||||
-- Work with tabs -------------------------------------------------------------
|
||||
-- List tabs
|
||||
H.list_tabs = function()
|
||||
local tabs = {}
|
||||
for _, buf_id in ipairs(vim.api.nvim_list_bufs()) do
|
||||
if H.is_buffer_in_minitabline(buf_id) then
|
||||
local tab = { buf_id = buf_id }
|
||||
tab['hl'] = H.construct_highlight(buf_id)
|
||||
tab['tabfunc'] = H.construct_tabfunc(buf_id)
|
||||
tab['label'], tab['label_extender'] = H.construct_label_data(buf_id)
|
||||
|
||||
table.insert(tabs, tab)
|
||||
end
|
||||
end
|
||||
|
||||
H.tabs = tabs
|
||||
end
|
||||
|
||||
H.is_buffer_in_minitabline = function(buf_id) return vim.bo[buf_id].buflisted end
|
||||
|
||||
-- Tab's highlight group
|
||||
H.construct_highlight = function(buf_id)
|
||||
local hl_type
|
||||
if buf_id == vim.api.nvim_get_current_buf() then
|
||||
hl_type = 'Current'
|
||||
elseif vim.fn.bufwinnr(buf_id) > 0 then
|
||||
hl_type = 'Visible'
|
||||
else
|
||||
hl_type = 'Hidden'
|
||||
end
|
||||
if vim.bo[buf_id].modified then hl_type = 'Modified' .. hl_type end
|
||||
|
||||
return string.format('%%#MiniTabline%s#', hl_type)
|
||||
end
|
||||
|
||||
-- Tab's clickable action (if supported)
|
||||
H.construct_tabfunc = function(buf_id)
|
||||
if H.tablineat > 0 then
|
||||
return string.format('%%%d@MiniTablineSwitchBuffer@', buf_id)
|
||||
else
|
||||
return ''
|
||||
end
|
||||
end
|
||||
|
||||
-- Tab's label and label extender
|
||||
H.construct_label_data = function(buf_id)
|
||||
local label, label_extender
|
||||
|
||||
local bufpath = vim.api.nvim_buf_get_name(buf_id)
|
||||
if bufpath ~= '' then
|
||||
-- Process path buffer
|
||||
label = vim.fn.fnamemodify(bufpath, ':t')
|
||||
label_extender = H.make_path_extender(buf_id)
|
||||
else
|
||||
-- Process unnamed buffer
|
||||
label = H.make_unnamed_label(buf_id)
|
||||
label_extender = function(x) return x end
|
||||
end
|
||||
|
||||
return label, label_extender
|
||||
end
|
||||
|
||||
H.make_path_extender = function(buf_id)
|
||||
-- Add parent to current label (if possible)
|
||||
return function(label)
|
||||
local full_path = vim.api.nvim_buf_get_name(buf_id)
|
||||
-- Using `vim.pesc` prevents effect of problematic characters (like '.')
|
||||
local pattern = string.format('[^%s]+%s%s$', vim.pesc(H.path_sep), vim.pesc(H.path_sep), vim.pesc(label))
|
||||
return string.match(full_path, pattern) or label
|
||||
end
|
||||
end
|
||||
|
||||
-- Work with unnamed buffers --------------------------------------------------
|
||||
-- Unnamed buffers are tracked in `H.unnamed_buffers_seq_ids` for
|
||||
-- disambiguation. This table is designed to store 'sequential' buffer
|
||||
-- identifier. This approach allows to have the following behavior:
|
||||
-- - Create three unnamed buffers.
|
||||
-- - Delete second one.
|
||||
-- - Tab label for third one remains the same.
|
||||
H.make_unnamed_label = function(buf_id)
|
||||
local label
|
||||
if vim.bo[buf_id].buftype == 'quickfix' then
|
||||
-- It would be great to differentiate for buffer `buf_id` between quickfix
|
||||
-- and location lists but it seems there is no reliable way to do so.
|
||||
-- The only one is to use `getwininfo(bufwinid(buf_id))` and look for
|
||||
-- `quickfix` and `loclist` fields, but that fails if buffer `buf_id` is
|
||||
-- not visible.
|
||||
label = '*quickfix*'
|
||||
else
|
||||
label = H.is_buffer_scratch(buf_id) and '!' or '*'
|
||||
end
|
||||
|
||||
-- Possibly add tracking id
|
||||
local unnamed_id = H.get_unnamed_id(buf_id)
|
||||
if unnamed_id > 1 then label = string.format('%s(%d)', label, unnamed_id) end
|
||||
|
||||
return label
|
||||
end
|
||||
|
||||
H.is_buffer_scratch = function(buf_id)
|
||||
local buftype = vim.bo[buf_id].buftype
|
||||
return (buftype == 'acwrite') or (buftype == 'nofile')
|
||||
end
|
||||
|
||||
H.get_unnamed_id = function(buf_id)
|
||||
-- Use existing sequential id if possible
|
||||
local seq_id = H.unnamed_buffers_seq_ids[buf_id]
|
||||
if seq_id ~= nil then return seq_id end
|
||||
|
||||
-- Cache sequential id for currently unnamed buffer `buf_id`
|
||||
H.unnamed_buffers_seq_ids[buf_id] = vim.tbl_count(H.unnamed_buffers_seq_ids) + 1
|
||||
return H.unnamed_buffers_seq_ids[buf_id]
|
||||
end
|
||||
|
||||
-- Work with labels -----------------------------------------------------------
|
||||
H.finalize_labels = function()
|
||||
-- Deduplicate
|
||||
local nonunique_tab_ids = H.get_nonunique_tab_ids()
|
||||
while #nonunique_tab_ids > 0 do
|
||||
local nothing_changed = true
|
||||
|
||||
-- Extend labels
|
||||
for _, buf_id in ipairs(nonunique_tab_ids) do
|
||||
local tab = H.tabs[buf_id]
|
||||
local old_label = tab.label
|
||||
tab.label = tab.label_extender(tab.label)
|
||||
if old_label ~= tab.label then nothing_changed = false end
|
||||
end
|
||||
|
||||
if nothing_changed then break end
|
||||
|
||||
nonunique_tab_ids = H.get_nonunique_tab_ids()
|
||||
end
|
||||
|
||||
-- Format labels
|
||||
local config = H.get_config()
|
||||
|
||||
-- - Ensure cached `get_icon` for `default_format` (for better performance)
|
||||
H.ensure_get_icon(config)
|
||||
|
||||
-- - Apply formatting
|
||||
local format = config.format or MiniTabline.default_format
|
||||
for _, tab in pairs(H.tabs) do
|
||||
tab.label = format(tab.buf_id, tab.label)
|
||||
end
|
||||
end
|
||||
|
||||
---@return table Array of `H.tabs` ids which have non-unique labels.
|
||||
---@private
|
||||
H.get_nonunique_tab_ids = function()
|
||||
-- Collect tab-array-id per label
|
||||
local label_tab_ids = {}
|
||||
for i, tab in ipairs(H.tabs) do
|
||||
local label = tab.label
|
||||
if label_tab_ids[label] == nil then
|
||||
label_tab_ids[label] = { i }
|
||||
else
|
||||
table.insert(label_tab_ids[label], i)
|
||||
end
|
||||
end
|
||||
|
||||
-- Collect tab-array-ids with non-unique labels
|
||||
return H.tbl_flatten(vim.tbl_filter(function(x) return #x > 1 end, label_tab_ids))
|
||||
end
|
||||
|
||||
-- Fit tabline to maximum displayed width -------------------------------------
|
||||
H.fit_width = function()
|
||||
H.update_center_buf_id()
|
||||
|
||||
-- Compute label width data
|
||||
local center_offset = 1
|
||||
local tot_width = 0
|
||||
for _, tab in pairs(H.tabs) do
|
||||
-- Use `nvim_strwidth()` and not `:len()` to respect multibyte characters
|
||||
tab.label_width = vim.api.nvim_strwidth(tab.label)
|
||||
tab.chars_on_left = tot_width
|
||||
|
||||
tot_width = tot_width + tab.label_width
|
||||
|
||||
if tab.buf_id == H.center_buf_id then
|
||||
-- Make right end of 'center tab' to be always displayed in center in
|
||||
-- case of truncation
|
||||
center_offset = tot_width
|
||||
end
|
||||
end
|
||||
|
||||
local display_interval = H.compute_display_interval(center_offset, tot_width)
|
||||
|
||||
H.truncate_tabs_display(display_interval)
|
||||
end
|
||||
|
||||
H.update_center_buf_id = function()
|
||||
local cur_buf = vim.api.nvim_get_current_buf()
|
||||
if H.is_buffer_in_minitabline(cur_buf) then H.center_buf_id = cur_buf end
|
||||
end
|
||||
|
||||
H.compute_display_interval = function(center_offset, tabline_width)
|
||||
-- left - first character to be displayed (starts with 1)
|
||||
-- right - last character to be displayed
|
||||
-- Conditions to be satisfied:
|
||||
-- 1) right - left + 1 = math.min(tot_width, tabline_width)
|
||||
-- 2) 1 <= left <= tabline_width; 1 <= right <= tabline_width
|
||||
|
||||
local tot_width = vim.o.columns - vim.api.nvim_strwidth(H.tabpage_section)
|
||||
|
||||
-- Usage of `math.floor` is crucial to avoid non-integer values which might
|
||||
-- affect total width of output tabline string.
|
||||
-- Using `floor` instead of `ceil` has effect when `tot_width` is odd:
|
||||
-- - `floor` makes "true center" to be between second to last and last label
|
||||
-- character (usually non-space and space).
|
||||
-- - `ceil` - between last character of center label and first character of
|
||||
-- next label (both whitespaces).
|
||||
local right = math.min(tabline_width, math.floor(center_offset + 0.5 * tot_width))
|
||||
local left = math.max(1, right - tot_width + 1)
|
||||
right = left + math.min(tot_width, tabline_width) - 1
|
||||
|
||||
return { left, right }
|
||||
end
|
||||
|
||||
H.truncate_tabs_display = function(display_interval)
|
||||
local display_left, display_right = display_interval[1], display_interval[2]
|
||||
|
||||
local tabs = {}
|
||||
for _, tab in ipairs(H.tabs) do
|
||||
local tab_left = tab.chars_on_left + 1
|
||||
local tab_right = tab.chars_on_left + tab.label_width
|
||||
if (display_left <= tab_right) and (tab_left <= display_right) then
|
||||
-- Process tab that should be displayed (even partially)
|
||||
local n_trunc_left = math.max(0, display_left - tab_left)
|
||||
local n_trunc_right = math.max(0, tab_right - display_right)
|
||||
|
||||
-- Take desired amount of characters starting from `n_trunc_left`
|
||||
tab.label = vim.fn.strcharpart(tab.label, n_trunc_left, tab.label_width - n_trunc_right)
|
||||
|
||||
table.insert(tabs, tab)
|
||||
end
|
||||
end
|
||||
|
||||
H.tabs = tabs
|
||||
end
|
||||
|
||||
-- Concatenate tabs into single tablien string --------------------------------
|
||||
H.concat_tabs = function()
|
||||
-- NOTE: it is assumed that all padding is incorporated into labels
|
||||
local t = {}
|
||||
for _, tab in ipairs(H.tabs) do
|
||||
-- Escape '%' in labels
|
||||
table.insert(t, ('%s%s%s'):format(tab.hl, tab.tabfunc, tab.label:gsub('%%', '%%%%')))
|
||||
end
|
||||
|
||||
-- Usage of `%X` makes filled space to the right 'non-clickable'
|
||||
local res = ('%s%%X%%#MiniTablineFill#'):format(table.concat(t, ''))
|
||||
|
||||
-- Add tabpage section
|
||||
local position = H.get_config().tabpage_section
|
||||
if H.tabpage_section ~= '' then
|
||||
if position == 'left' then res = ('%%#MiniTablineTabpagesection#%s%s'):format(H.tabpage_section, res) end
|
||||
if position == 'right' then
|
||||
-- Use `%=` to make it stick to right hand side
|
||||
res = ('%s%%=%%#MiniTablineTabpagesection#%s'):format(res, H.tabpage_section)
|
||||
end
|
||||
end
|
||||
|
||||
return res
|
||||
end
|
||||
|
||||
-- Utilities ------------------------------------------------------------------
|
||||
H.ensure_get_icon = function(config)
|
||||
if not config.show_icons then
|
||||
-- Show no icon
|
||||
H.get_icon = nil
|
||||
elseif H.get_icon ~= nil then
|
||||
-- Cache only once
|
||||
return
|
||||
elseif _G.MiniIcons ~= nil then
|
||||
-- Prefer 'mini.icons'
|
||||
H.get_icon = function(name) return (_G.MiniIcons.get('file', name)) end
|
||||
else
|
||||
-- Try falling back to 'nvim-web-devicons'
|
||||
local has_devicons, devicons = pcall(require, 'nvim-web-devicons')
|
||||
if not has_devicons then return end
|
||||
-- Use basename because it makes exact file name matching work
|
||||
H.get_icon = function(name) return (devicons.get_icon(vim.fn.fnamemodify(name, ':t'), nil, { default = true })) end
|
||||
end
|
||||
end
|
||||
|
||||
-- TODO: Remove after compatibility with Neovim=0.9 is dropped
|
||||
H.tbl_flatten = vim.fn.has('nvim-0.10') == 1 and function(x) return vim.iter(x):flatten(math.huge):totable() end
|
||||
or vim.tbl_flatten
|
||||
|
||||
return MiniTabline
|
||||
2311
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/test.lua
Normal file
2311
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/test.lua
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,201 @@
|
||||
--- *mini.trailspace* Trailspace (highlight and remove)
|
||||
--- *MiniTrailspace*
|
||||
---
|
||||
--- MIT License Copyright (c) 2021 Evgeni Chasnovski
|
||||
---
|
||||
--- ==============================================================================
|
||||
---
|
||||
--- Features:
|
||||
--- - Highlighting is done only in modifiable buffer by default, only in Normal
|
||||
--- mode, and stops in Insert mode and when leaving window.
|
||||
---
|
||||
--- - Trim all trailing whitespace with |MiniTrailspace.trim()|.
|
||||
---
|
||||
--- - Trim all trailing empty lines with |MiniTrailspace.trim_last_lines()|.
|
||||
---
|
||||
--- # Setup ~
|
||||
---
|
||||
--- This module needs a setup with `require('mini.trailspace').setup({})`
|
||||
--- (replace `{}` with your `config` table). It will create global Lua table
|
||||
--- `MiniTrailspace` which you can use for scripting or manually (with
|
||||
--- `:lua MiniTrailspace.*`).
|
||||
---
|
||||
--- See |MiniTrailspace.config| for `config` structure and default values.
|
||||
---
|
||||
--- You can override runtime config settings locally to buffer inside
|
||||
--- `vim.b.minitrailspace_config` which should have same structure as
|
||||
--- `MiniTrailspace.config`. See |mini.nvim-buffer-local-config| for more details.
|
||||
---
|
||||
--- # Highlight groups ~
|
||||
---
|
||||
--- * `MiniTrailspace` - highlight group for trailing space.
|
||||
---
|
||||
--- To change any highlight group, modify it directly with |:highlight|.
|
||||
---
|
||||
--- # Disabling ~
|
||||
---
|
||||
--- To disable, set `vim.g.minitrailspace_disable` (globally) or
|
||||
--- `vim.b.minitrailspace_disable` (for a buffer) to `true`. Considering high
|
||||
--- number of different scenarios and customization intentions, writing exact
|
||||
--- rules for disabling module's functionality is left to user. See
|
||||
--- |mini.nvim-disabling-recipes| for common recipes. Note: after disabling
|
||||
--- there might be highlighting left; it will be removed after next
|
||||
--- highlighting update (see |events| and `MiniTrailspace` |augroup|).
|
||||
|
||||
-- Module definition ==========================================================
|
||||
local MiniTrailspace = {}
|
||||
local H = {}
|
||||
|
||||
--- Module setup
|
||||
---
|
||||
---@param config table|nil Module config table. See |MiniTrailspace.config|.
|
||||
---
|
||||
---@usage >lua
|
||||
--- require('mini.trailspace').setup() -- use default config
|
||||
--- -- OR
|
||||
--- require('mini.trailspace').setup({}) -- replace {} with your config table
|
||||
--- <
|
||||
MiniTrailspace.setup = function(config)
|
||||
-- Export module
|
||||
_G.MiniTrailspace = MiniTrailspace
|
||||
|
||||
-- Setup config
|
||||
config = H.setup_config(config)
|
||||
|
||||
-- Apply config
|
||||
H.apply_config(config)
|
||||
|
||||
-- Define behavior
|
||||
H.create_autocommands(config)
|
||||
|
||||
-- Create default highlighting
|
||||
H.create_default_hl()
|
||||
|
||||
-- Initialize highlight (usually takes effect during startup)
|
||||
vim.defer_fn(MiniTrailspace.highlight, 0)
|
||||
end
|
||||
|
||||
--- Module config
|
||||
---
|
||||
--- Default values:
|
||||
---@eval return MiniDoc.afterlines_to_code(MiniDoc.current.eval_section)
|
||||
MiniTrailspace.config = {
|
||||
-- Highlight only in normal buffers (ones with empty 'buftype'). This is
|
||||
-- useful to not show trailing whitespace where it usually doesn't matter.
|
||||
only_in_normal_buffers = true,
|
||||
}
|
||||
--minidoc_afterlines_end
|
||||
|
||||
-- Module functionality =======================================================
|
||||
--- Highlight trailing whitespace in current window
|
||||
MiniTrailspace.highlight = function()
|
||||
-- Highlight only in normal mode
|
||||
if H.is_disabled() or vim.fn.mode() ~= 'n' then
|
||||
MiniTrailspace.unhighlight()
|
||||
return
|
||||
end
|
||||
|
||||
-- Possibly work only in normal buffers
|
||||
if H.get_config().only_in_normal_buffers and not H.is_buffer_normal() then return end
|
||||
|
||||
-- Don't add match id on top of existing one
|
||||
if H.get_match_id() ~= nil then return end
|
||||
|
||||
vim.fn.matchadd('MiniTrailspace', [[\s\+$]])
|
||||
end
|
||||
|
||||
--- Unhighlight trailing whitespace in current window
|
||||
MiniTrailspace.unhighlight = function()
|
||||
-- Use `pcall` because there is an error if match id is not present. It can
|
||||
-- happen if something else called `clearmatches`.
|
||||
pcall(vim.fn.matchdelete, H.get_match_id())
|
||||
end
|
||||
|
||||
--- Trim trailing whitespace
|
||||
MiniTrailspace.trim = function()
|
||||
-- Save cursor position to later restore
|
||||
local curpos = vim.api.nvim_win_get_cursor(0)
|
||||
-- Search and replace trailing whitespace
|
||||
vim.cmd([[keeppatterns %s/\s\+$//e]])
|
||||
vim.api.nvim_win_set_cursor(0, curpos)
|
||||
end
|
||||
|
||||
--- Trim last blank lines
|
||||
MiniTrailspace.trim_last_lines = function()
|
||||
local n_lines = vim.api.nvim_buf_line_count(0)
|
||||
local last_nonblank = vim.fn.prevnonblank(n_lines)
|
||||
if last_nonblank < n_lines then vim.api.nvim_buf_set_lines(0, last_nonblank, n_lines, true, {}) end
|
||||
end
|
||||
|
||||
-- Helper data ================================================================
|
||||
-- Module default config
|
||||
H.default_config = vim.deepcopy(MiniTrailspace.config)
|
||||
|
||||
-- Helper functionality =======================================================
|
||||
-- Settings -------------------------------------------------------------------
|
||||
H.setup_config = function(config)
|
||||
-- General idea: if some table elements are not present in user-supplied
|
||||
-- `config`, take them from default config
|
||||
vim.validate({ config = { config, 'table', true } })
|
||||
config = vim.tbl_deep_extend('force', vim.deepcopy(H.default_config), config or {})
|
||||
|
||||
vim.validate({ only_in_normal_buffers = { config.only_in_normal_buffers, 'boolean' } })
|
||||
|
||||
return config
|
||||
end
|
||||
|
||||
H.apply_config = function(config) MiniTrailspace.config = config end
|
||||
|
||||
H.create_autocommands = function(config)
|
||||
local augroup = vim.api.nvim_create_augroup('MiniTrailspace', {})
|
||||
|
||||
local au = function(event, pattern, callback, desc)
|
||||
vim.api.nvim_create_autocmd(event, { group = augroup, pattern = pattern, callback = callback, desc = desc })
|
||||
end
|
||||
|
||||
-- NOTE: Respecting both `WinEnter` and `BufEnter` seems to be useful to
|
||||
-- account of different order of handling buffer opening in new window.
|
||||
-- Notable example: 'nvim-tree' at commit a1600e5.
|
||||
au({ 'WinEnter', 'BufEnter', 'InsertLeave' }, '*', MiniTrailspace.highlight, 'Highlight')
|
||||
au({ 'WinLeave', 'BufLeave', 'InsertEnter' }, '*', MiniTrailspace.unhighlight, 'Unhighlight')
|
||||
|
||||
if config.only_in_normal_buffers then
|
||||
-- Add tracking of 'buftype' changing because it can be set after events on
|
||||
-- which highlighting is done. If not done, highlighting appears but
|
||||
-- disappears if buffer is reentered.
|
||||
au('OptionSet', 'buftype', H.track_normal_buffer, 'Track normal buffer')
|
||||
end
|
||||
end
|
||||
|
||||
H.create_default_hl = function() vim.api.nvim_set_hl(0, 'MiniTrailspace', { default = true, link = 'Error' }) end
|
||||
|
||||
H.is_disabled = function() return vim.g.minitrailspace_disable == true or vim.b.minitrailspace_disable == true end
|
||||
|
||||
H.get_config = function(config)
|
||||
return vim.tbl_deep_extend('force', MiniTrailspace.config, vim.b.minitrailspace_config or {}, config or {})
|
||||
end
|
||||
|
||||
H.track_normal_buffer = function()
|
||||
if not H.get_config().only_in_normal_buffers then return end
|
||||
|
||||
-- This should be used with 'OptionSet' event for 'buftype' option
|
||||
-- Empty 'buftype' means "normal buffer"
|
||||
if vim.v.option_new == '' then
|
||||
MiniTrailspace.highlight()
|
||||
else
|
||||
MiniTrailspace.unhighlight()
|
||||
end
|
||||
end
|
||||
|
||||
H.is_buffer_normal = function(buf_id) return vim.bo[buf_id or 0].buftype == '' end
|
||||
|
||||
H.get_match_id = function()
|
||||
-- NOTE: this can be replaced with more efficient custom tracking of id per
|
||||
-- window but it will have more edge cases (like won't update on manual
|
||||
-- `clearmatches()`)
|
||||
for _, match in ipairs(vim.fn.getmatches()) do
|
||||
if match.group == 'MiniTrailspace' then return match.id end
|
||||
end
|
||||
end
|
||||
|
||||
return MiniTrailspace
|
||||
1561
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/visits.lua
Normal file
1561
config/neovim/store/lazy-plugins/mini.nvim/lua/mini/visits.lua
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user