1

Update generated neovim config

This commit is contained in:
2024-08-15 14:28:54 +02:00
parent 07409c223d
commit 25cfcf2941
3809 changed files with 351157 additions and 0 deletions

View File

@ -0,0 +1,51 @@
name: Bug report
description: Report a problem
labels: [bug]
body:
- type: checkboxes
attributes:
label: Contributing guidelines
options:
- label: I have read [CONTRIBUTING.md](https://github.com/echasnovski/mini.nvim/blob/main/CONTRIBUTING.md)
required: true
- label: I have read [CODE_OF_CONDUCT.md](https://github.com/echasnovski/mini.nvim/blob/main/CODE_OF_CONDUCT.md)
required: true
- label: I have updated 'mini.nvim' to latest version
required: true
- type: input
attributes:
label: "Module(s)"
description: "List one or several modules this bug is coming from"
validations:
required: true
- type: textarea
attributes:
label: "Description"
description: "A short description of a problem"
validations:
required: true
- type: input
attributes:
label: "Neovim version"
description: "Something like `0.5`, `0.5.1`, Neovim nightly (please, include latest commit)"
validations:
required: true
- type: textarea
attributes:
label: "Steps to reproduce"
description: "Steps to reproduce using as minimal config as possible"
value: |
1. `nvim -nu minimal.lua`
2. ...
validations:
required: true
- type: textarea
attributes:
label: "Expected behavior"
description: "A description of behavior you expected"
- type: textarea
attributes:
label: "Actual behavior"
description: "A description of behavior you observed (feel free to include images, gifs, etc.)"
validations:
required: true

View File

@ -0,0 +1 @@
blank_issues_enabled: true

View File

@ -0,0 +1,24 @@
name: Feature request
description: Describe a feature you want to see
labels: [feature-request]
body:
- type: checkboxes
attributes:
label: Contributing guidelines
options:
- label: I have read [CONTRIBUTING.md](https://github.com/echasnovski/mini.nvim/blob/main/CONTRIBUTING.md)
required: true
- label: I have read [CODE_OF_CONDUCT.md](https://github.com/echasnovski/mini.nvim/blob/main/CODE_OF_CONDUCT.md)
required: true
- type: input
attributes:
label: "Module(s)"
description: "List one or several modules this feature is related to"
validations:
required: true
- type: textarea
attributes:
label: "Description"
description: "A concise and justified description of a feature"
validations:
required: true

View File

@ -0,0 +1,2 @@
- [ ] I have read [CONTRIBUTING.md](https://github.com/echasnovski/mini.nvim/blob/main/CONTRIBUTING.md)
- [ ] I have read [CODE_OF_CONDUCT.md](https://github.com/echasnovski/mini.nvim/blob/main/CODE_OF_CONDUCT.md)

View File

@ -0,0 +1,85 @@
name: Linting and style checking
on:
push:
branches-ignore: [ sync, stable ]
pull_request:
branches-ignore: [ sync, stable ]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.actor }}
cancel-in-progress: true
jobs:
stylua:
name: Formatting
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: JohnnyMorganz/stylua-action@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
version: v0.19.0
# CLI arguments
args: --color always --check .
gendoc:
name: Document generation
runs-on: ubuntu-latest
steps:
- name: Install Neovim
uses: rhysd/action-setup-vim@v1
with:
neovim: true
- uses: actions/checkout@v4
- name: Generate documentation
run: make --silent documentation
- name: Check for changes
run: if [[ -n $(git status -s) ]]; then exit 1; fi
lintcommit:
name: Lint commits
runs-on: ubuntu-latest
env:
LINTCOMMIT_STRICT: true
steps:
- name: Install Neovim
uses: rhysd/action-setup-vim@v1
with:
neovim: true
- uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ github.event.pull_request.head.sha || github.ref }}
- name: Lint new commits
run: make --silent lintcommit-ci
case-sensitivity:
name: File case sensitivity
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Check Case Sensitivity
uses: credfeto/action-case-checker@v1.2.1
checkout:
# Test possibility of checking out and setting up 'mini.nvim' on non-Linux
# This can guard from possible problems:
# - Long file names (particularly from reference screenshots).
name: Test checkout
strategy:
fail-fast: false
matrix:
os: [windows-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Setup neovim
uses: rhysd/action-setup-vim@v1
with:
# Uses latest stable by default
neovim: true
- name: Test setup
run: make basic_setup

View File

@ -0,0 +1,39 @@
name: Run tests
on:
push:
branches-ignore: [ sync, stable ]
paths: [ 'colors/**', 'lua/**', 'tests/**' ]
pull_request:
branches-ignore: [ sync, stable ]
paths: [ 'colors/**', 'lua/**', 'tests/**' ]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.actor }}
cancel-in-progress: true
jobs:
build:
name: Run tests
timeout-minutes: 10
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
neovim_version: ['v0.8.3', 'v0.9.5', 'v0.10.0', 'nightly']
# include:
# - os: macos-latest
# neovim_version: v0.10.0
# - os: windows-latest
# neovim_version: v0.10.0
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Setup neovim
uses: rhysd/action-setup-vim@v1
with:
neovim: true
version: ${{ matrix.neovim_version }}
- name: Run tests
run: make test

View File

@ -0,0 +1,2 @@
doc/tags
dual

View File

@ -0,0 +1,19 @@
repos:
- repo: local
hooks:
- id: stylua
name: StyLua
language: system
entry: stylua
types: [lua]
- id: gendocs
name: Gendocs
language: system
entry: make --silent documentation
types: [lua]
- id: lintcommit
name: LintCommit
language: system
entry: nvim
args: ['--headless', '--noplugin', '-u', 'scripts/lintcommit.lua', '--']
stages: ['commit-msg']

View File

@ -0,0 +1,7 @@
column_width = 120
line_endings = "Unix"
indent_type = "Spaces"
indent_width = 2
quote_style = "AutoPreferSingle"
call_parentheses = "Always"
collapse_simple_statement = "Always"

View File

@ -0,0 +1 @@
dual

View File

@ -0,0 +1,682 @@
# Version 0.13.0.9000
- Stop official support of Neovim 0.7.
- Update help files to use code blocks with language annotation, as it results in a better code highlighting. Implies enabled tree-sitter highlighting in 'help' filetype:
- It is default in Neovim>=0.10.
- Tree-sitter parser is built-in in Neovim 0.9.x, needs manual enabling via `vim.treesitter.start()`.
- Has visual regressions on Neovim 0.8.0 and 0.8.1 without enabled tree-sitter (code blocks are highlighted as normal text). Use 0.8.2 or newer.
- Universally prefer 'mini.icons' module over 'nvim-tree/nvim-web-devicons'.
## mini.doc
- BREAKING FEATURE: update `afterlines_to_code()` to result into Lua code block in help file by using `>lua` at the start instead of `>`. NOTE: users need enabled `help` tree-sitter parser (which is default on Neovim>=0.9) for code blocks to have proper highlighting.
## mini.extra
- FEATURE: update `pickers.oldfiles()` to have `current_dir` option which if `true` shows files only from picker's working directory. By @abeldekat, PR #997.
- FEATURE: make `git_hunks`, `list`, and `lsp` pickers show icons. Scopes `document_symbol` and `workspace_symbol` in `lsp` picker show icon based on LSP kind (requires set up 'mini.icons'), others - based on path data.
- BREAKING FEATURE: use "│" as line/position separator instead of ":". This aligns with changes in 'mini.pick' and makes line/position more easily visible.
## mini.hipatterns
- BREAKING FEATURE: update `compute_hex_color_group()` to compute based on combination of `hex_color` and `style`, opposed to just `hex_color`. This allows simultaneous usage of several styles in user's custom highlighters.
## mini.hues
- FEATURE: implement `apply_palette()` (to compliment `make_palette()`) providing a way to tweak applied palette before applying it.
## mini.files
- FEATURE: prefer using 'mini.icons' as icon provider.
## mini.icons
- Introduction of a new module.
## mini.misc
- FEATURE: implement `setup_termbg_sync()` to set up terminal background synchronization (removes possible "frame" around current Neovim instance).
## mini.pick
- FEATURE: prefer using 'mini.icons' as icon provider.
- BREAKING: encoding line or position in string items has changed:
- Use "\0" (null character; use "\000" form if it is in a string before digit) instead of ":" as delimiter. This makes it work with files similar to ":" position encoding (like "time_12:34:56"). This only matters for custom sources which provide line or position in string items.
- Update `default_show()` to display "│" character instead of "\0" in item's string representation (previously was ":"). In particular, this changes how line/position is displayed in `grep` and `grep_live` built-in pickers. This change was done because "│" is more visible as separator.
## mini.starter
- BREAKING: change filetype of Starter buffer from 'starter' to 'ministarter'. This is a more robust value and more aligned with other modules.
## mini.statusline
- BREAKING FEATURE: update `section_fileinfo()` to show non-empty filetype even in not normal buffers (like plugin's scratch buffers, help, quickfix, etc.). Previously it showed nothing, which was a mistake as filetype can be a valuable information.
- FEATURE: prefer using 'mini.icons' as icon provider for `section_fileinfo()`.
## mini.surround
- BREAKING FEATURE: adding surrounding in linewise mode now also ignores trailing whitespace on the last line (same as it ignores indent on the first line).
## mini.tabline
- FEATURE: prefer using 'mini.icons' as icon provider.
# Version 0.13.0
## mini.comment
- BREAKING FEATURE: blank lines are now completely ignored when deciding the toggling action. In practice this means that if target block consists only from commented and/or blank lines, it will be uncommented rather than commented.
- BREAKING: Whitespace in comment parts is now treated more explicitly. In particular:
- Default `options.pad_comment_parts = true` now more explicitly means that any value of 'commentstring' is transformed so that comment parts have exactly single space inner padding.
Example: any `/*%s*/`, ` /* %s */ `, or `/* %s */` is equivalent to having `/* %s */`.
- Detection of whether consecutive lines are commented or not does not depend on whitespace in comment parts. Uncommenting is first attempted with exact comment parts and falls back on trying its trimmed parts.
Example of toggling comment on single line with `/* %s */` 'commentstring' value:
- `/* this is commented */` -> `this is commented`.
- `/*this is also commented */` -> `this is also commented ` (notice trailing space).
- Commenting blank lines is done with trimmed comments parts, while uncommenting explicitly results into empty lines.
- FEATURE: Support dot-repeat after initial commenting is done for visual selection; repeating is done for same relative range.
## mini.deps
- FEATURE: add `MiniDepsMsgBreaking` highlight group for messages indicating a breaking change in a conventional commit style.
## mini.diff
- Introduction of a new module.
## mini.files
- FEATURE: add new `MiniFilesExplorerOpen` and `MiniFilesExplorerClose` events.
## mini.git
- Introduction of a new module.
## mini.hues
- BREAKING FEATURE: update some highlight groups for better usability:
- `DiffChange` and `DiffText` - make changed diff lines have colored background.
- `Folded` - make folds differ from `CursorLine`.
- `QuickFixLine` - make current quickfix item differ from `CursorLine`.
## mini.map
- FEATURE: add `gen_integration.diff()` which highlights general diff hunks from 'mini.diff'.
## mini.pick
- BREAKING: stop trying to parse path for special format ("path:row" and "path:row:col") if supplied inside a table item. This made impossible working with paths containing ":".
- FEATURE: respect general URI format for paths inside table items.
- Update `builtin.files()` to use table items when string item might be ambiguous.
## mini.starter
- Explicitly block all events in `open()` during startup for a better performance.
## mini.statusline
- BREAKING: `section_diagnostics()` now depends only on defined diagnostic. This means:
- Something is shown **only** if there is any diagnostic actually present in the buffer. No diagnostic entries - nothing is shown.
Previously it did not show if there was no LSP servers attached (as initially diagnostics came only from LSP) or buffer was not normal.
- Fallback icon is "Diag" instead of "LSP".
- FEATURE: `section_diagnostics()` now supports `signs` table option to customize signs for severity levels.
- BREAKING FEATURE: `section_git()` now prefers using data from 'mini.git' with fallback on pure HEAD data from 'lewis6991/gistigns.nvim'.
- FEATURE: add `section_diff()` to show data from 'mini.diff' with fallback on diff data from 'lewis6991/gistigns.nvim'.
- FEATURE: add `section_lsp()` to show indicator of LSP servers attached to the buffer.
- BREAKING FEATURE: update default active content:
- Add `section_diff()` (shows diff data near  icon) following refactor of `section_git()`.
- Add `section_lsp()` (shows number of attached LSP servers near 󰰎 icon) following refactor of `section_diagnostics()`.
## mini.tabline
- FEATURE: Implement `config.format` for custom label formatting.
## mini.test
- BREAKING FEATURE: child process is now created with extra `--headless --cmd "set lines=24 columns=80"` arguments making it headless but still reasonably similar to fully functioning Neovim during interactive usage. This change should generally not break a lot of things, while enabling a faster and more robust test execution.
# Version 0.12.0
## mini.basics
- BREAKING: Remove `<C-z>` mapping, as it is more useful in most terminal emulators for suspending Neovim process (to later resume with `fg` command). To correct latest misspelled word, use mappings like this:
```lua
vim.keymap.set('n', '<C-z>', '[s1z=', { desc = 'Correct latest misspelled word' })
vim.keymap.set('i', '<C-z>', '<C-g>u<Esc>[s1z=`]a<C-g>u', { desc = 'Correct latest misspelled word' })
```
- FEATURE: Add `tab:> ` to 'listchars' option when `options.extra_ui` is set. This prevents showing `^I` instead of a tab and actual value comes from Neovim's default.
- FEATURE: Set `termguicolors` only on Neovim<0.10, as later versions should have it on by default (if terminal emulator supports it).
## mini.comment
- FEATURE: Hooks are now called with data about commenting action.
## mini.deps
Introduction of a new module.
## mini.doc
- BREAKING: Stop using `:echo` to display messages and warnings in favor of `vim.notify()`.
- BREAKING: Update default `write_post` hook to not display current time in success message.
- Update to include space before `~` in generated section headings.
## mini.files
- FEATURE: Update `go_in()` to have `close_on_file` option.
- Show warning if action is set to override existing path.
## mini.hues
- BREAKING FEATURE: Update verbatim text (`@text.literal` and `@markup.raw`) color to be distinctive instead of dimmed.
- FEATURE: Add support for new standard tree-sitter captures on Neovim>=0.10 (see https://github.com/neovim/neovim/pull/27067).
## mini.misc
- Update `bench_time()` to use `vim.loop.hrtime()` (as better designed for benchmarking) instead of `vim.loop.gettimeofday()`.
## mini.notify
Introduction of a new module.
## mini.pick
- FEATURE: Implement `window.prompt_cursor` and `window.prompt_prefix` config options.
- FEATURE: Update `builtin.help()` to use tree-sitter highlighting (if there is any).
## mini.sessions
- FEATURE: Update `read()` to first `write()` current session (if there is any).
## mini.starter
- FEATURE: Add `sections.pick()` with 'mini.pick' pickers.
## mini.statusline
- BREAKING FEATURE: Add `search_count` section to default active content.
## mini.visits
Introduction of a new module.
# Version 0.11.0
## mini.base16
- BREAKING: Stop supporting deprecated 'HiPhish/nvim-ts-rainbow2'.
## mini.bufremove
- BREAKING FEATURE: Applying `delete()` and `wipeout()` without `force` in a modified buffer now asks for confirmation instead of declining and showing message.
## mini.clue
- FEATURE: `config.window.config` now can be callable returning window config.
## mini.comment
- FEATURE: Implement `config.mappings.comment_visual` to configure mapped keys in Visual mode.
## mini.completion
- FEATURE: Start adding `C` flag to `shortmess` option on Neovim>=0.9. By @yamin-shihab, PR #554.
## mini.extra
Introduction of a new module.
## mini.files
- BREAKING: Opening file which is present in unlisted buffer now makes the buffer listed.
- BREAKING: Highlight in preview now is not enabled if file is sufficiently large.
- FEATURE: Explorer now tracks if focus is lost and properly closes on detection.
- FEATURE: Implement `MiniFilesCursorLine` highlight group.
## mini.hipatterns
- FEATURE: Allow `pattern` in highlighter definitions to be an array to highlight several patterns under the same highlighter name.
- FEATURE: Implement `extmark_opts` in highlighter definitions for a more control over extmarks placed at matches.
- BREAKING: Field `priority` in highlighter definitions is soft deprecated in favor of `extmark_opts = { priority = <value> }`.
- FEATURE: Update `compute_hex_color_group()` to allow `style = 'fg'`.
- FEATURE: Update `gen_highlighter.hex_color()` to allow `style = 'inline'` (requires Neovim>=0.10 with support of inline extmarks).
- FEATURE: Implement `get_matches()` to get buffer matches.
## mini.hues
- BREAKING: Stop supporting deprecated 'HiPhish/nvim-ts-rainbow2'.
## mini.map
- FEATURE: Implement `config.window.zindex` to configure z-index of map window.
## mini.misc
- FEATURE: `setup_auto_root()` and `find_root()` now have `fallback` argument to be applied when no root is found with `vim.fn.find()`.
## mini.pick
Introduction of a new module.
## mini.starter
- FEATURE: `show_path` in `sections.recent_files()` can now be callable for more control on how full path is displayed.
## mini.test
- BREAKING: Error in any "pre" hook now leads to test case not being executed (with note).
- BREAKING FEATURE: `child.get_screenshot()` now by default calls `:redraw` prior to computing screenshot. Can be disabled by new `opts.redraw` argument.
- FEATURE: New method `child.lua_func()` can execute simple functions inside child process and return the result (stasjok, #437).
- FEATURE: `expect.reference_screenshot()` now has `ignore_lines` option allowing to ignore specified lines during screenshot compare.
# Version 0.10.0
## mini.ai
- FEATURE: Allow `vis_mode` field in custom texobject region to force Visual mode with which textobject is selected.
## mini.animate
- FEATURE: Add `MiniAnimateNormalFloat` highlight group to tweak highlighting of `open` and `close` animations.
## mini.base16
- FEATURE: Add 'HiPhish/rainbow-delimiters.nvim' integration.
## mini.basics
- BREAKING: Remove `<C-w>` mapping in Terminal mode, as it is more useful inside terminal emulator itself.
## mini.bracketed
- FEATURE: Add `add_to_jumplist` option to relevant targets (which move cursor and don't already add to jumplist).
## mini.bufremove
- BREAKING: Create normal buffer instead of scratch when there is no reasonable target to focus (#394).
## mini.clue
Introduction of a new module.
## mini.files
Introduction of a new module.
## mini.hues
- FEATURE: Add 'HiPhish/rainbow-delimiters.nvim' integration.
## mini.jump2d
- FEATURE: Add `gen_union_spotter()` to allow combining separate spotters into one.
## mini.operators
Introduction of a new module.
## mini.pairs
- FEATURE: Allow `false` in `config.mappings` to not map the key.
## mini.surround
- FEATURE: Update `add` (`sa`) with ability to replicate left and right parts by respecting `[count]`. In Normal mode two kinds of `[count]` is respected: one for operator (replicates left and right parts) and one for textobject/motion. In Visual mode `[count]` replicates parts.
# Version 0.9.0
- Stop official support of Neovim 0.6.
- Use Lua API to create autocommands. Stop exporting functions only related to autocommands.
- Use Lua API to create default highlight groups.
- Use `vim.keymap` to deal with mappings. Stop exporting functions only related to mappings.
- Add 'randomhue' color scheme.
## mini.base16
- FEATURE: Add new integrations:
- Lsp semantic tokens.
- 'folke/lazy.nvim'.
- 'folke/noice.nvim'.
- 'kevinhwang91/nvim-ufo'.
- BREAKING FEATURE: Stop supporting archived 'p00f/nvim-ts-rainbow' in favor of 'HiPhish/nvim-ts-rainbow2'.
## mini.basics
- Add dot-repeat support for adding empty lines (`go` and `gO` mappings).
## mini.colors
Introduction of a new module.
## mini.comment
- FEATURE: Use tree-sitter information about locally active language to infer 'commentstring' option value.
- FEATURE: Add `options.custom_commentstring` option for a more granular customization of comment structure.
- FEATURE: Add `get_commentstring()` function representing built-in logic of computing relevant 'commentstring'.
## mini.hipatterns
Introduction of a new module.
## mini.hues
Introduction of a new module.
# Version 0.8.0
- Add and implement design principle for silencing module by setting `config.silent = true`. It is now present in modules capable of showing non-error feedback:
- mini.ai
- mini.align
- mini.basics
- mini.bufremove
- mini.doc
- mini.jump
- mini.jump2d
- mini.starter
- mini.surround
- mini.test
## mini.bracketed
Introduction of a new module.
## mini.comment
- FEATURE: Add `options.start_of_line` option which controls whether to recognize as comment only lines without indent.
- FEATURE: Add `options.ignore_blank_line` option which controls whether to ignore blank lines.
- FEATURE: Add `options.pad_comment_parts` option which controls whether to ensure single space pad for comment leaders.
## mini.doc
- FEATURE: Add `config.hooks.write_pre` hook to be executed before writing to a file.
## mini.indentscope
- FEATURE: Add `MiniIndentscopeSymbolOff` highlight group to be used if scope's indent is not multiple of 'shiftwidth'.
- FEATURE: Add `draw.priority` option to control priority of scope line draw.
## mini.jump2d
- FEATURE: Add `view.n_steps_ahead` option which controls how many steps ahead to show. Appearance is controlled by new `MiniJump2dSpotAhead` highlight group.
- FEATURE: Add `view.dim` option which controls whether to dim lines with at least one jump spot. Appearance is controlled by new `MiniJump2dDim` highlight group.
- FEATURE: Add `MiniJump2dSpotUnique` highlight group to be used for spots with unique label for next step.
## mini.pairs
- FEATURE: Both `MiniPairs.br()` and `MiniPairs.cr()` can now take a key which will be used instead of default `<BS>` and `<CR>`.
## mini.sessions
- FEATURE: `setup()` now creates global directory at path `config.directory` if it doesn't exist.
- All actions now keep list of detected sessions up to date.
## mini.splitjoin
Introduction of a new module.
## mini.surround
- FEATURE: Add `respect_selection_type` option which, when enabled, makes adding and deleting surrounding respect selection type:
- Linewise adding places surrounding parts on separate lines while indenting surrounded lines once.
- Deleting surrounding which looks like a result of linewise adding will act to revert it: delete lines with surrounding parts and dedent surrounded lines once.
- Blockwise adding places surrounding parts on whole edges, not only start and end of selection.
# Version 0.7.0
- Start dual distribution. Every module is now distributed both as part of 'mini.nvim' library and as standalone plugin (in separate git repository).
## mini.ai
- BREAKING FEATURE: In `MiniAi.gen_spec.argument()` option `separators` (plural; array of characters) is soft deprecated in favor of `separator` (singular; Lua pattern) option.
## mini.animate
Introduction of new module.
## mini.basics
Introduction of a new module.
## mini.completion
- BREAKING: `MiniCompletion.config.window_dimensions` is renamed to `MiniCompletion.config.window` to be able to handle more general configuration.
- FEATURE: Add `MiniCompletion.config.window.info.border` and `MiniCompletion.config.window.signature.border` which can be used to define border of info and signature floating windows respectively.
## mini.indentscope
- BREAKING: `MiniIndentscopePrefix` is now not used (deprecated). It was initially introduced as a way to properly show scope indicator on empty lines. It had a drawback of overshadowing 'listchars' symbols (see #125) and vertical guides from 'lukas-reineke/indent-blankline.nvim'. As the other implementation approach was found by @mivort (see #161), `MiniIndentscopePrefix` is no longer needed and no overshadowing is done.
- BREAKING: `MiniIndentscope.gen_animation` is now a table (for consistency with other `gen_*` functions in 'mini.nvim'). See "Migrate from function type" section of `:h MiniIndentscope.gen_animation`. Calling it as function will be available until next release.
## mini.misc
- FEATURE: Add `MiniMisc.setup_auto_root()` and `MiniMisc.find_root()` for root finding functionality. NOTE: requires Neovim>=0.8.
- FEATURE: Add `MiniMisc.setup_restore_cursor()` for automatically restoring latest cursor position on file reopen. By @cryptomilk, PR #198.
## mini.move
Introduction of new module.
# Version 0.6.0
- Stop official support of Neovim 0.5.
- Make all messages use colors and not cause hit-enter-prompt.
## mini.align
Introduction of new module.
## mini.base16
- FEATURE: Add support for many plugin integrations.
- FEATURE: Implement `MiniBase16.config.plugins` for configuring plugin integrations.
- BREAKING: Change some 'mini.nvim' highlights:
- `MiniCompletionActiveParameter` now highlights with background instead of underline.
- `MiniJump2dSpot` now explicitly defined to use plugin's palette.
- `MiniStarterItemPrefix` and `MiniStarterQuery` are now bold for better visibility.
- BREAKING: Update highlight for changed git diff to be more visible and to comply more with general guidelines.
## mini.jump
- BREAKING: Allow cursor to be positioned past the end of previous/current line (#113).
## mini.map
Introduction of new module.
## mini.starter
- Item evaluation is now prepended with query reset, as it is rarely needed any more (#105).
- All hooks are now called with `(content, buf_id)` signature allowing them properly use current window layout.
## mini.surround
- BREAKING FEATURE: update 'mini.surround' to share as much with 'mini.ai' as possible. This provides more integrated experience while enabling more useful features. Details:
- Custom surrounding specification for input action has changed. Instead of `{ find = <string>, extract = <string> }` it is now `{ <function or composed pattern> }`. Previous format will work until the next release. See more in help file.
- Algorithm for finding surrounding is now more powerful. It allows searching for more complex surroundings (via composed patterns or array of region pairs) and respects `v:count`.
- Multiline input and output surroundings are now supported.
- Opening brackets (`(`, `[`, `{`, `<`) now include whitespace in surrounding: input surrounding selects all inner edge whitespace, output surrounding is padded with single space.
- Surrounding identifier `i` ("interactive") is soft deprecated in favor of `?` ("user prompt").
- New surrounding aliases:
- `b` for "brackets". Input - any of balanced `()`, `[]` `{}`. Output - `()`.
- `q` for "quotes". Input - any of `"`, `'`, `` ` ``. Output - `""`.
- Three new search methods `'prev'`, `'next'`, and `'nearest'` for finding non-covering previous and next surrounding.
- BREAKING FEATURE: Implement "last"/"next" extended mappings which force `'prev'` or `'next'` search method. Controlled with `config.mappings.suffix_last` and `config.mappings.suffix_next`respectively. This also means that custom surroundings with identifier equal to "last"/"next" mappings suffixes (defaults to 'l' and 'n') will work only with long enough delay after typing action mapping.
- FEATURE: Implement `MiniSurround.gen_spec` with generators of common surrounding specifications (like `MiniSurround.gen_spec.input.treesitter` for tree-sitter based input surrounding).
# Version 0.5.0
- Update all tests to use new 'mini.test' module.
- FEATURE: Implement buffer local configuration. This is done with `vim.b.mini*_config` buffer variables.
- Add new `minicyan` color scheme.
## mini.ai
Introduction of new module.
## mini.comment
- FEATURE: Now hooks can be used to terminate further actions by returning `false` (#108).
## mini.indentscope
- BREAKING: Soft deprecate `vim.b.miniindentscope_options` in favor of using `options` field of `miniindentscope_config`.
## mini.sessions
- FEATURE: Hooks are now called with active session data as argument.
## mini.starter
- FEATURE: Now it is possible to open multiple Starter buffers at the same time (#82). This comes with several changes which won't affect most users:
- BREAKING: `MiniStarter.content` is deprecated. Use `MiniStarter.get_content()`.
- All functions dealing with Starter buffer now have `buf_id` as argument (no breaking behavior).
## mini.statusline
- FEATURE: Implement `config.use_icons` which controls whether to use icons by default.
## mini.test
Introduction of new module.
## mini.trailspace
- FEATURE: Implement `MiniTrailspace.trim_last_lines()`.
# Version 0.4.0
- Update all modules to supply mapping description for Neovim>=0.7.
- Add new module 'mini.jump2d'.
- Cover all modules with extensive tests.
## mini.comment
- FEATURE: Implement `config.hooks` with `pre` and `post` hooks (executed before and after successful commenting). Fixes #50, #59.
## mini.completion
- Implement support for `additionalTextEdits` (issue #61).
## mini.jump
- FEATURE: Implement idle timeout to stop jumping automatically (@annenpolka, #56).
- FEATURE: Implement `MiniJump.state`: table with useful model-related information.
- BREAKING: Soft deprecate `config.highlight_delay` in favor of `config.delay.highlight`.
- Update process of querying target symbol: show help message after delay, allow `<C-c>` to stop selecting target.
## mini.jump2d
Introduction of new module.
## mini.pairs
- Create mappings for `<BS>` and `<CR>` in certain mode only after some pair is registered in that mode.
## mini.sessions
- FEATURE: Implement `MiniSessions.select()` to select session interactively and perform action on it.
- FEATURE: Implement `config.hooks` to execute hook functions before and after successful action.
- BREAKING: All feedback about incorrect behavior is now an error instead of message notifications.
## mini.starter
- Allow `config.header` and `config.footer` be any value, which will be converted to string via `tostring()`.
- Update query logic to not allow queries which result into no items.
- Add `<C-n>` and `<C-p>` to default mappings.
## mini.statusline
- BREAKING: change default icon for `MiniStatusline.section_diagnostics()` from ﯭ to  due to former having issues in some terminal emulators.
## mini.surround
- FEATURE: Implement `config.search_method`.
- FEATURE: Implement custom surroundings via `config.custom_surroundings`.
- FEATURE: Implement `MiniSurround.user_input()`.
- BREAKING: Deprecate `config.funname_pattern` option in favor of manually modifying `f` surrounding.
- BREAKING: Always move cursor to the right of left surrounding in `add()`, `delete()`, and `replace()` (instead of moving only if it was on the same line as left surrounding).
- Update process of getting user input: allow `<C-c>` to cancel and make empty string a valid input.
## mini.tabline
- FEATURE: Implement `config.tabpage_section`.
- BREAKING: Show listed buffers also in case of multiple tabpages (instead of using builtin behavior).
- Show quickfix/loclist buffers with special `*quickfix*` label.
# Version 0.3.0
- Update all modules to have annotations formatted for 'mini.doc'.
## mini.cursorword
- Current word under cursor now can be highlighted differently.
## mini.doc
Introduction of new module.
## mini.indentscope
Introduction of new module.
## mini.starter
- Implement `MiniStarter.set_query()` and make `<Esc>` mapping for resetting query.
# Version 0.2.0
## mini.base16
- Use new `Diagnostic*` highlight groups in Neovim 0.6.0.
## mini.comment
- Respect tab indentation (#20).
## mini.jump
Introduction of new module.
## mini.pairs
- Implement pair registration with custom mapping functions. More detailed:
- Implement `MiniPairs.map()`, `MiniPairs.map_buf()`, `MiniPairs.unmap()`, `MiniPairs.unmap_buf()` to (un)make mappings for pairs which automatically register them for `<BS>` and `<CR>`. Note, that this has a minor break of previous behavior: now `MiniPairs.bs()` and `MiniPairs.cr()` don't have any input argument. But default behavior didn't change.
- Allow setting global pair mappings inside `config` of `MiniPairs.setup()`.
## mini.sessions
Introduction of new module.
## mini.starter
Introduction of new module.
## mini.statusline
- Implement new section `MiniStatusline.section_searchcount()`.
- Update `section_diagnostics` to use `vim.diagnostic` in Neovim 0.6.0.
# Version 0.1.0
- Initial stable version.

View File

@ -0,0 +1,132 @@
# Contributor Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, caste, color, religion, or sexual
identity and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the overall
community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or advances of
any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email address,
without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
`evgeni <dot> chasnovski |at| gmail >dot< com` .
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series of
actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or permanent
ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within the
community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.1, available at
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
Community Impact Guidelines were inspired by
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
For answers to common questions about this code of conduct, see the FAQ at
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
[https://www.contributor-covenant.org/translations][translations].
[homepage]: https://www.contributor-covenant.org
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
[Mozilla CoC]: https://github.com/mozilla/diversity
[FAQ]: https://www.contributor-covenant.org/faq
[translations]: https://www.contributor-covenant.org/translations

View File

@ -0,0 +1,282 @@
# Contributing
Thank you for your willingness to contribute to 'mini.nvim'. It means a lot!
You can make contributions in the following ways:
- **Mention it** somehow to help reach broader audience. This helps a lot.
- **Create a GitHub issue**. It can be one of the following types:
- **Bug report**. Describe your actions in a reproducible way along with their effect and what you expected should happen. Before making one, please make your best efforts to make sure that it is not an intended behavior (not described in documentation as such).
- **Feature request**. A concise and justified description of what one or several modules should be able to do. Before making one, please make your best efforts to make sure that it is not a feature that won't get implemented (these should be described in documentation; for example: block comments in 'mini.comment').
- **Create a pull request (PR)**. It can be one of the following types:
- **Code related**. For example, fix a bug or implement a feature. **Before even starting one, please make sure that it is aligned with project vision and goals**. The best way to do so is to receive positive feedback from maintainer on your initiative in one of the GitHub issues (existing or created by you). Please, make sure to regenerate latest help file and that all tests pass (see later sections).
- **Documentation related**. For example, fix typo/wording in 'README.md', code comments or annotations (which are used to generate Neovim documentation; see later section). Feel free to make these without creating a GitHub issue.
- **Add plugin integration to 'mini.base16' and 'mini.hues' modules**.
- **Add explicit support to other colorschemes**. Every 'mini.nvim' module supports any colorscheme right out of the box. This is done by making most highlight groups be linked to a semantically similar builtin highlight group. Other groups are hard-coded based on personal preference. However, these choices might be out of tune with a particular colorscheme. Updating as many colorschemes as possible to have explicit 'mini.nvim' support is highly appreciated. For your convenience, there is a list of all highlight groups in later section of this file.
- **Participate in [discussions](https://github.com/echasnovski/mini.nvim/discussions)**.
All well-intentioned, polite, and respectful contributions are always welcome! Thanks for reading this!
## Commit messages
- Try to make commit message as concise as possible while giving enough information about nature of a change. Think about whether it will be easy to understand in one year time when browsing through commit history.
- Single commit should change either zero or one module, or affect all modules (i.e. enforcing some universal rule but not necessarily change files). Changes for two or more modules should be split in several module-specific commits.
- Use [Conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) style:
- Messages should have the following structure:
```
<type>[optional scope][!]: <description>
<empty line>
[optional body]
<empty line>
[optional footer(s)]
```
- `<type>` is **mandatory** and can be one of:
- `ci` - change in how automation (GitHub actions, dual distribution scripts, etc.) is done.
- `docs` - change in user facing documentation (help, README, CONTRIBUTING, etc.).
- `feat` - adding new user facing feature.
- `fix` - resolving user facing issue.
- `refactor` - change in code or documentation that should not affect users.
- `style` - change in convention of how something should be done (formatting, wording, etc.) and its effects.
- `test` - change in tests.
For temporary commits which later should be squashed (when working on PR, for example), use `fixup` type.
- `[optional scope]`, if present, should be done in parenthesis `()`. If commit changes single module (as it usually should), using scope with module name is **mandatory**. If commit enforces something for all modules, use `ALL` scope.
- Breaking change, if present, should be expressed with `!` before `:`.
- `<description>` is a change overview in imperative, present tense ("change" not "changed" nor "changes"). Should result into first line under 72 characters. Should start with not capitalized word and NOT end with sentence ending punctuation (i.e. one of `.,?!;`).
- `[optional body]`, if present, should contain details and motivation about the change in plain language. Should be formatted to have maximum 80 characters in line.
- `[optional footer(s)]`, if present, should be instruction(s) to Git or Github. Use "Resolve #xxx" on separate line if this commit resolves issue or PR.
- Use module's function and field names without module's name. Like `add()` and not `MiniSurround.add()`.
Examples:
```
feat(deps): add folds in update confirmation buffer
```
```
fix(jump): make operator not delete one character if target is not found
One main goal is to do that in a dot-repeatable way, because this is very
likely to be repeated after an unfortunate first try.
Resolve #688
```
```
refactor(bracketed): do not source 'vim.treesitter' on `require()`
Although less explicit, this considerably reduces startup footprint of
'mini.bracketed' in isolation.
```
```
feat(hues)!: update verbatim text to be distinctive
```
```
test(ALL): update screenshots to work on Nightly
```
### Automated commit linting
- To lint messages of already done commits, execute `scripts/lintcommit-ci.sh <git-log-range>`. For example, to lint currently latest commit use `scripts/lintcommit-ci.sh HEAD~..HEAD`.
- To lint commit message before doing commit, install [`pre-commit`](https://pre-commit.com/#install) and enable it with `pre-commit install --hook-type commit-msg` (from the root directory). NOTE: requires `nvim` executable. If it throws (usually descriptive) error - recommit with proper message.
## Generating help file
If your contribution updates annotations used to generate help file, please regenerate it. You can make this with one of the following (assuming current directory being project root):
- From command line execute `make documentation`.
- Inside Neovim instance run `:luafile scripts/minidoc.lua` or `:lua require('mini.doc').generate()`.
## Testing
If your contribution updates code and you use Linux (not Windows or MacOS), please make sure that it doesn't break existing tests. If it adds new functionality or fixes a recognized bug, add new test case(s). There are two ways of running tests:
- From command line:
- Execute `make test` to run all tests (with `nvim` as executable).
- Execute `FILE=tests/test_xxx.lua make test_file` to run tests only from file `tests/test_xxx.lua` (with `nvim` as executable).
- If you have multiple Neovim executables (say, `nvim_07`, `nvim_08`, `nvim_09`, `nvim_010`), you can use `NVIM_EXEC` variable to tests against multiple versions like this:
`NVIM_EXEC="nvim_07 nvim_08 nvim_09 nvim_010" make test` or `NVIM_EXEC="nvim_07 nvim_08 nvim_09 nvim_010" FILE=tests/test_xxx.lua make test_file`.
- Inside Neovim instance execute `:lua require('mini.test').setup(); MiniTest.run()` to run all tests or `:lua require('mini.test').setup(); MiniTest.run_file()` to run tests only from current buffer.
This plugin uses 'mini.test' to manage its tests. For a more hands-on introduction, see [TESTING.md](TESTING.md).
**Notes**:
- If you have Windows or MacOS and want to contribute code related change, make your best effort to not break existing behavior. It will later be tested automatically after making Pull Request. The reason for this distinction is that tests are not well designed to be run on those operating systems.
- If new functionality relies on an external dependency (`git` CLI tool, LSP server, etc.), use mocking (writing Lua code which emulates dependency usage as close as reasonably possible). For examples, take a look at tests for 'mini.pick', 'mini.completion', and 'mini.statusline'.
- There is a certain number of tests that are flaky (i.e. will sometimes report an error due to other reasons than actual functionality being broke). It is usually the ones which test time related functionality (i.e. that certain action was done after specific amount of delay).
A commonly used way to know if the test is flaky is that it fails on non-nightly Neovim version yet there were no changes to its tested module after it had passed in the past. For example, some 'mini.animate' test is shown to break but there were no changes to it since test passed in CI couple of days before.
In case there is some test breaking which reasonably should not, rerun that test (or the whole file) at least several times.
## Formatting
This project uses [StyLua](https://github.com/JohnnyMorganz/StyLua) version 0.19.0 for formatting Lua code. Before making changes to code, please:
- [Install StyLua](https://github.com/JohnnyMorganz/StyLua#installation). NOTE: use `v0.19.0`.
- Format with it. Currently there are two ways to do this:
- Manually run `stylua .` from the root directory of this project.
- Install [`pre-commit`](https://pre-commit.com/#install) and enable it with `pre-commit install` (from the root directory). This will auto-format relevant code before making commits.
## List of highlight groups
Here is a list of all highlight groups defined inside 'mini.nvim' modules. See documentation in 'doc' directory to find out what they are used for.
- 'mini.animate':
- `MiniAnimateCursor`
- `MiniAnimateNormalFloat`
- 'mini.clue':
- `MiniClueBorder`
- `MiniClueDescGroup`
- `MiniClueDescSingle`
- `MiniClueNextKey`
- `MiniClueNextKeyWithPostkeys`
- `MiniClueSeparator`
- `MiniClueTitle`
- 'mini.completion':
- `MiniCompletionActiveParameter`
- 'mini.cursorword':
- `MiniCursorword`
- `MiniCursorwordCurrent`
- 'mini.deps':
- `MiniDepsChangeAdded`
- `MiniDepsChangeRemoved`
- `MiniDepsHint`
- `MiniDepsInfo`
- `MiniDepsMsgBreaking`
- `MiniDepsPlaceholder`
- `MiniDepsTitle`
- `MiniDepsTitleError`
- `MiniDepsTitleSame`
- `MiniDepsTitleUpdate`
- 'mini.diff':
- `MiniDiffSignAdd`
- `MiniDiffSignChange`
- `MiniDiffSignDelete`
- `MiniDiffOverAdd`
- `MiniDiffOverChange`
- `MiniDiffOverContext`
- `MiniDiffOverDelete`
- 'mini.files':
- `MiniFilesBorder`
- `MiniFilesBorderModified`
- `MiniFilesCursorLine`
- `MiniFilesDirectory`
- `MiniFilesFile`
- `MiniFilesNormal`
- `MiniFilesTitle`
- `MiniFilesTitleFocused`
- 'mini.hipatterns':
- `MiniHipatternsFixme`
- `MiniHipatternsHack`
- `MiniHipatternsNote`
- `MiniHipatternsTodo`
- 'mini.icons':
- `MiniIconsAzure`
- `MiniIconsBlue`
- `MiniIconsCyan`
- `MiniIconsGreen`
- `MiniIconsGrey`
- `MiniIconsOrange`
- `MiniIconsPurple`
- `MiniIconsRed`
- `MiniIconsYellow`
- 'mini.indentscope':
- `MiniIndentscopeSymbol`
- `MiniIndentscopeSymbolOff`
- 'mini.jump':
- `MiniJump`
- 'mini.jump2d':
- `MiniJump2dDim`
- `MiniJump2dSpot`
- `MiniJump2dSpotAhead`
- `MiniJump2dSpotUnique`
- 'mini.map':
- `MiniMapNormal`
- `MiniMapSymbolCount`
- `MiniMapSymbolLine`
- `MiniMapSymbolView`
- 'mini.notify':
- `MiniNotifyBorder`
- `MiniNotifyNormal`
- `MiniNotifyTitle`
- 'mini.operators':
- `MiniOperatorsExchangeFrom`
- 'mini.pick':
- `MiniPickBorder`
- `MiniPickBorderBusy`
- `MiniPickBorderText`
- `MiniPickIconDirectory`
- `MiniPickIconFile`
- `MiniPickHeader`
- `MiniPickMatchCurrent`
- `MiniPickMatchMarked`
- `MiniPickMatchRanges`
- `MiniPickNormal`
- `MiniPickPreviewLine`
- `MiniPickPreviewRegion`
- `MiniPickPrompt`
- 'mini.starter':
- `MiniStarterCurrent`
- `MiniStarterFooter`
- `MiniStarterHeader`
- `MiniStarterInactive`
- `MiniStarterItem`
- `MiniStarterItemBullet`
- `MiniStarterItemPrefix`
- `MiniStarterSection`
- `MiniStarterQuery`
- 'mini.statusline':
- `MiniStatuslineDevinfo`
- `MiniStatuslineFileinfo`
- `MiniStatuslineFilename`
- `MiniStatuslineInactive`
- `MiniStatuslineModeCommand`
- `MiniStatuslineModeInsert`
- `MiniStatuslineModeNormal`
- `MiniStatuslineModeOther`
- `MiniStatuslineModeReplace`
- `MiniStatuslineModeVisual`
- 'mini.surround':
- `MiniSurround`
- 'mini.tabline':
- `MiniTablineCurrent`
- `MiniTablineFill`
- `MiniTablineHidden`
- `MiniTablineModifiedCurrent`
- `MiniTablineModifiedHidden`
- `MiniTablineModifiedVisible`
- `MiniTablineTabpagesection`
- `MiniTablineVisible`
- 'mini.test':
- `MiniTestEmphasis`
- `MiniTestFail`
- `MiniTestPass`
- 'mini.trailspace':
- `MiniTrailspace`

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 Evgeni Chasnovski
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,188 @@
# Maintaining
This document contains knowledge about specifically maintaining 'mini.nvim'. It assumes general knowledge about how Open Source and GitHub issues/PRs work.
See [CONTRIBUTING.md](CONTRIBUTING.md) for how to generate help files, run tests, and format.
## General advice
- Follow common boilerplate code as much as possible when creating new module, as it makes easier to use "search and replace" in the long term. This includes:
- Documentation at the beginning: describing module, its setup, highlight groups, similar plugins, disabling, `setup()`, and `config`.
- Create and use `H` helper table at the beginning to allow having exported code written before helpers (severely improves readability).
- Structure of `setup()` function with its helper functions: `H.setup_config()`, `H.apply_config()`, `H.create_autocommands()`, `H.create_default_hl()`, `H.create_user_commands()`.
- Use module's `H.get_config()` and `H.is_disabled()` helpers. They both should respect buffer local configuration.
- From time to time some test cases will break on Neovim Nightly. This is usually due to the following reasons:
- There was an intended change in Neovim Nightly to which affected module(s) should adapt. Update module and/or tests.
- There was a change in Neovim Nightly disrupting only tests (usually screenshots due to changed way of how highlight attributes are computed). Update test: ideally so that it passes on all versions, but testing some parts only on Nightly is allowed if needed (usually by regenerating screenshot on Nightly and verifying it only on versions starting from it).
- There was an unintended change in Neovim Nightly which breaks functionality it should not break. Create an issue in ['neovim/neovim' repo](https://github.com/neovim/neovim). If the issue is not resolved for a long-ish time (i.e. more than a week) try to make tests pass and/or adapt the code to new behavior.
## Maintainer setup
Mandatory:
- Have `nvim` executable for latest stable release.
- Install [`git`](https://www.git-scm.com).
- Install [`StyLua`](https://github.com/JohnnyMorganz/StyLua) with version described in [CONTRIBUTING.md](CONTRIBUTING.md).
- Install [`make`](https://www.gnu.org/software/make/).
Recommended:
- Have executables for all supported Neovim versions. For example, `nvim_07`, `nvim_08`, `nvim_09`, `nvim_010`. This is useful for running tests on multiple versions.
- Install [`lua-language-server`](https://github.com/LuaLS/lua-language-server).
- Install [`pre-commit`](https://pre-commit.com/#install) and enable it with `pre-commit install` and `pre-commit install --hook-type commit-msg` (run from repository's root).
- Set up 'mini.doc' and 'mini.test' and make mappings for the following frequently used commands:
- `'<Cmd>lua MiniDoc.generate()<CR>'` - to generate documentation.
- `'<Cmd>lua MiniTest.run_at_location()<CR>'` - to run test under cursor.
- `'<Cmd>lua MiniTest.run_file()<CR>'` - to run current test file.
## Supported Neovim versions
Aim for supporting 4 latest minor Neovim releases: current stable, current Nightly, and two latest stable releases.
For example, if 0.9.x is current stable, then all latest patch versions of 0.7, 0.8, 0.9 should be supported plus Nightly (0.10.0).
NOTE: some modules can have less supported versions during their release **only** if it is absolutely necessary for the core functionality.
## Dual distribution
Modules of 'mini.nvim' are distributed both as part of 'mini.nvim' repository and each one in its standalone repository. All development takes place in 'mini.nvim' while being synced to standalone ones. This is done by having special `sync` branch which points to the latest commit which was synced to standalone repositories.
Usual workflow involves performing these steps after every commit in 'mini.nvim':
- Check out to `main` branch.
- Ensure there are no immediate defects. Usually it means to wait until all CI checks passed.
- Run `make dual_sync`. This should:
- Create 'dual' directory if doesn't exist yet.
- Pull standalone repositories in 'dual/repos'.
- Create patches in 'dual/patches' and apply them for standalone repositories.
See 'scripts/dual_sync.sh' for more details.
- Run `make dual_log` to make sure that all and correct patches were applied. If some commit touches files from several modules, it results into commits for every affected standalone repository.
- Run `make dual_push`. This should:
- Push updates for affected standalone repositories.
- Clean up 'dual/patches'.
- Update `sync` branch to point to latest commit and push it to `origin`.
## Typical workflow for adding change
- Solve the problem.
- If change is in code, write test which breaks before problem is solved and passes after.
- If change introduces new config setting, consult with [dedicated checklist](#adding-new-config-settings).
- If change is worth to be seen by users (notable/breaking feature/fix), update 'CHANGELOG.md' following formatting from previous versions.
- Make sure that all tests in affected module(s) pass in all supported versions. See [Maintainer setup](#maintainer-setup) and ['Testing' section in CONTRIBUTING.md](CONTRIBUTING.md#testing).
- Stage and commit changes into a separate Git branch. Push the branch.
- Make sure that all CI pass.
- Merge branch into `main` branch. Push `main`.
- Make sure that all CI pass (again).
- Synchronize dual distribution:
- `make dual_sync` to sync.
- `make dual_log` and look at changes which are about to be applied to standalone repositories. Make sure that they are what you'd expect.
- `make dual_push` to push changes to standalone repositories.
## Typical workflow for processing a GitHub issue
- Add label with module name issue is about (if any). If issue is worded politely and/or with much details, thank user for opening an issue.
- Make sure the underlying problem is valid, i.e. it can be reproduced and the root cause is in this project. If it can not be reproduced, politely explain that and ask for more reproduction details. If the cause is not related to the project, politely explain that, close an issue, and direct towards the real root cause.
- Check already existing issues for possible duplicates. If there is at least one, review its reasoning before making decision about the current issue.
- Decide whether and how an issue should be resolved. Use ["General principles"](README.md#general-principles), module's help and code documentation while making the decision.
- If decision is to not resolve, politely explain that and close an issue (possibly mentioning similar reasoning in the past).
- If decision is to resolve, resolve the issue while putting `Resolve #xxx` at the bottom of commit message.
## Typical workflow for processing GitHub pull request
- Add label with module name pull request (PR) is about (if any). If PR is worded politely, thank user for doing that.
- Make sure the PR is valid, i.e. resolves an issue or adds a feature any of which aligns with the project. Ideally, it should have been agreed in the prior created issue (as per [CONTRIBUTING.md](CONTRIBUTING.md)).
- Review PR code and iterate towards making it have enough code quality. Use first steps of ["Typical workflow for adding change"](#typical-workflow-for-adding-change) as reference. **Note**: if what is left to do requires some overly specific project knowledge (i.e. can be done _much_ quicker if you know how, but requires non-trivial amount of reading/discovering first time), consider merging PR in a new separate branch and finish it manually (usually with preserving original commit authorship).
- When change is of enough quality, merge it and proceed treating it as regular change.
## Stopping support for old Neovim version
Begin the process of stopping official support for outdated Neovim version shortly after (week or two) the release of the new stable one. Usually it is stopping support for Neovim 0.x (say, 0.8) shortly after the release of 0.(x+3).0 (say, 0.11.0). The deprecation should be done in two stages:
- Stage 1, soft deprecation (to notify old version users about upcoming support drop):
- Add version of the following code snippet at the beginning of `setup()` function body in **every** module:
```lua
-- TODO: Remove after Neovim=0.8 support is dropped
if vim.fn.has('nvim-0.8') == 0 then
vim.notify(
'(mini.ai) Neovim<0.9 is soft deprecated (module works but not supported).'
.. ' It will be deprecated after next "mini.nvim" release (module might not work).'
.. ' Please update your Neovim version.'
)
end
```
- Modify CI to not test on Neovim 0.x.
- Update README and repo description to indicate new oldest supported Neovim version.
- Wait for a considerable amount of time (at least about a month) *and* a new 'mini.nvim' stable release (so that there is no actual deprecation in the stable release).
- Stage 2, deprecation:
- Remove all notification snippets added in Stage 1.
- Adjust code that is conditioned on `vim.fn.has('nvim-0.x')`.
- Adjust code/comments/documentation that contains any combination of `Neovim{<,<=,=,>=,>}{0.x,0.(x+1)}` (like `Neovim<0.x`, `Neovim>=0.(x+1)`, etc.).
- Add entry "Stop official support of Neovim 0.x." in 'CHANGELOG.md' at the start of current development version block.
## Adding new config settings
- Add code which uses new setting.
- Add default value to `Mini*.config` definition.
- Update module's `H.setup_config()` with type check of new setting.
- Update tests to test default config value and its type check.
- Regenerate help file.
- Update module's README in 'readmes' directory.
- Possibly update demo for it to be aligned with current config values.
- Update 'CHANGELOG.md'. In module's section of current version add line starting with `- FEATURE: Implement ...`.
## Adding new color scheme plugin integration
- Update color scheme module file in a way similar to other already added plugins:
- Add definitions for highlight groups.
- Add plugin entry in a list of supported plugins in help annotations.
- Add plugin entry in a module's README.
- Regenerate documentation (see [corresponding section in CONTRIBUTING.md](CONTRIBUTING.md#generating-help-file)).
## Adding new module
- Add Lua source code in 'lua' directory.
- Add tests in 'tests' directory. Use 'tests/dir-xxx' name for module-specific non-test helpers.
- Update 'lua/init.lua' to mention new module: both in initial table of contents and list of modules.
- Update 'scripts/basic-setup_init.lua' to include new module.
- Update 'scripts/dual_sync.sh' to include new module.
- Update 'scripts/minidoc.lua' to generate separate help file.
- Generate help files.
- Add README to 'readmes' directory. NOTE: comment out mentions of `stable` branch, as it won't work during beta-testing.
- Update main README to mention new module in table of contents.
- Update 'CHANGELOG.md' to mention introduction of new module.
- Update 'CONTRIBUTING.md' to mention new highlight groups (if there are any).
- Commit changes with message 'feat(xxx): add NEW MODULE'. NOTE: it is cleaner to synchronize standalone repositories prior to this commit.
- If there are new highlight groups, follow up with adding explicit support in color scheme modules.
- Make standalone plugin:
- Create new empty GitHub repository. Disable Issues and limit PRs.
- Synchronize standalone repositories. It should have created new git repository with single initial commit.
- Make sure that all tracked files are synchronized. For list of tracked files see 'scripts/dual_sync.sh'. Initially they are 'doc/mini-xxx.txt', 'lua/mini/xxx.lua', 'LICENSE', and 'readmes/mini-xxx.md' (copied to be 'README.md' in standalone repository).
- Make sure that 'README.md' in standalone repository has appropriate relative links (see patch script).
- **Amend** initial commit and push.
- Push `main` and sync dual distribution.
## Making stable release
There is no clear guidelines for when a stable (minor) release should be made. Mostly "when if feels right" but "not too often". If it has to be put in words, it is something like "After 3 new modules have finished beta-testing or 4 months, whichever is sooner". No patch releases have been made yet.
Checklist:
- Check for `TODO`s about actions to be done *before* release.
- Update READMEs of new modules to mention `stable` branch.
- Bump version in 'CHANGELOG.md'. Commit.
- Checkout to `new_release` branch and push to check in CI. **Proceed only if it is successful**.
- Merge `new_release` to `main` and push it.
- Synchronize standalone repositories.
- Make annotated tag: `git tag -a v0.xx.0 -m 'Version 0.xx.0'`. Push it.
- Check that all CI has passed.
- Make GitHub release. Get description from copying entries of version's 'CHANGELOG.md' section.
- Move `stable` branch to point at new tag (`git branch --force stable` when on latest tag's commit). Push it.
- Release standalone repositories. It should be enough to use 'scripts/dual_release.sh' like so:
```
# REPLACE `xx` with your version number
TAG_NAME="v0.xx.0" TAG_MESSAGE="Version 0.xx.0" make dual_release
```
- Use development version in 'CHANGELOG.md' ('0.xx.0.9000'). Commit.
- Check for `TODO`s about actions to be done *after* release.

View File

@ -0,0 +1,46 @@
GROUP_DEPTH ?= 1
NVIM_EXEC ?= nvim
all: test documentation
test:
for nvim_exec in $(NVIM_EXEC); do \
printf "\n======\n\n" ; \
$$nvim_exec --version | head -n 1 && echo '' ; \
$$nvim_exec --headless --noplugin -u ./scripts/minimal_init.lua \
-c "lua require('mini.test').setup()" \
-c "lua MiniTest.run({ execute = { reporter = MiniTest.gen_reporter.stdout({ group_depth = $(GROUP_DEPTH) }) } })" ; \
done
test_file:
for nvim_exec in $(NVIM_EXEC); do \
printf "\n======\n\n" ; \
$$nvim_exec --version | head -n 1 && echo '' ; \
$$nvim_exec --headless --noplugin -u ./scripts/minimal_init.lua \
-c "lua require('mini.test').setup()" \
-c "lua MiniTest.run_file('$(FILE)', { execute = { reporter = MiniTest.gen_reporter.stdout({ group_depth = $(GROUP_DEPTH) }) } })" ; \
done
documentation:
$(NVIM_EXEC) --headless --noplugin -u ./scripts/minimal_init.lua -c "lua require('mini.doc').generate()" -c "qa!"
lintcommit-ci:
export LINTCOMMIT_STRICT=true && chmod u+x scripts/lintcommit-ci.sh && scripts/lintcommit-ci.sh
basic_setup:
$(NVIM_EXEC) --headless --noplugin -u ./scripts/basic-setup_init.lua
dual_sync:
chmod u+x scripts/dual_sync.sh && scripts/dual_sync.sh
dual_log:
chmod u+x scripts/dual_log.sh && scripts/dual_log.sh
dual_push:
chmod u+x scripts/dual_push.sh && scripts/dual_push.sh
git branch --force sync
git push origin sync
rm -r dual/patches
dual_release:
chmod u+x scripts/dual_release.sh && scripts/dual_release.sh "$(TAG_NAME)" "$(TAG_MESSAGE)"

View File

@ -0,0 +1,173 @@
<img src="logo.png" width="800em"/> <br>
<!-- badges: start -->
[![GitHub license](https://badgen.net/github/license/echasnovski/mini.nvim)](https://github.com/echasnovski/mini.nvim/blob/main/LICENSE)
[![GitHub tag](https://badgen.net/github/tag/echasnovski/mini.nvim)](https://github.com/echasnovski/mini.nvim/tags/)
[![Current version](https://badgen.net/badge/Current%20version/development/cyan)](https://github.com/echasnovski/mini.nvim/blob/main/CHANGELOG.md)
<!-- badges: end -->
Library of 40+ independent Lua modules improving overall [Neovim](https://github.com/neovim/neovim) (version 0.8 and higher) experience with minimal effort. They all share same configuration approaches and general design principles.
Think about this project as "Swiss Army knife" among Neovim plugins: it has many different independent tools (modules) suitable for most common tasks. Each module can be used separately without any startup and usage overhead.
If you want to help this project grow but don't know where to start, check out [contributing guides](CONTRIBUTING.md) or leave a Github star for 'mini.nvim' project and/or any its standalone Git repositories.
## Table of contents
- [Installation](#installation)
- [Modules](#modules)
- [General principles](#general-principles)
- [Plugin colorschemes](#plugin-colorschemes)
- [Planned modules](#planned-modules)
## Installation
There are two branches to install from:
- `main` (default, **recommended**) will have latest development version of plugin. All changes since last stable release should be perceived as being in beta testing phase (meaning they already passed alpha-testing and are moderately settled).
- `stable` will be updated only upon releases with code tested during public beta-testing phase in `main` branch.
Here are code snippets for some common installation methods:
- Manually with `git clone` (compatible with [mini.deps](https://github.com/echasnovski/mini.nvim/blob/main/readmes/mini-deps.md)):
```lua
-- Put this at the top of 'init.lua'
local path_package = vim.fn.stdpath('data') .. '/site'
local mini_path = path_package .. '/pack/deps/start/mini.nvim'
if not vim.loop.fs_stat(mini_path) then
vim.cmd('echo "Installing `mini.nvim`" | redraw')
local clone_cmd = {
'git', 'clone', '--filter=blob:none',
-- Uncomment next line to use 'stable' branch
-- '--branch', 'stable',
'https://github.com/echasnovski/mini.nvim', mini_path
}
vim.fn.system(clone_cmd)
vim.cmd('packadd mini.nvim | helptags ALL')
end
```
- With [folke/lazy.nvim](https://github.com/folke/lazy.nvim):
| Branch | Code snippet |
|--------|------------------------------------------------------|
| Main | `{ 'echasnovski/mini.nvim', version = false },` |
| Stable | `{ 'echasnovski/mini.nvim', version = '*' },` |
- With [junegunn/vim-plug](https://github.com/junegunn/vim-plug):
| Branch | Code snippet |
|--------|--------------------------------------------------------|
| Main | `Plug 'echasnovski/mini.nvim'` |
| Stable | `Plug 'echasnovski/mini.nvim', { 'branch': 'stable' }` |
- Every module is also distributed as a standalone Git repository. Check out module's information for more details.
**Important**: don't forget to call module's `setup()` (if required) to enable its functionality.
**Note**: if you are on Windows, there might be problems with too long file paths (like `error: unable to create file <some file name>: Filename too long`). Try doing one of the following:
- Enable corresponding git global config value: `git config --system core.longpaths true`. Then try to reinstall.
- Install plugin in other place with shorter path.
## Modules
If you are browsing without particular objective and don't know which module to look at:
- To improve your editing experience, start with 'mini.ai', 'mini.operators', 'mini.pairs', 'mini.surround'.
- To improve your general workflow, start with 'mini.bracketed', 'mini.files', 'mini.jump2d', 'mini.pick'.
- To make your Neovim more beautiful, start with 'mini.animate', 'mini.hues', 'mini.icons', 'mini.notify'.
| Module | Description | Overview | Details |
|------------------|------------------------------------------|---------------------------------------|---------------------------------------|
| mini.ai | Extend and create `a`/`i` textobjects | [README](readmes/mini-ai.md) | [Help file](doc/mini-ai.txt) |
| mini.align | Align text interactively | [README](readmes/mini-align.md) | [Help file](doc/mini-align.txt) |
| mini.animate | Animate common Neovim actions | [README](readmes/mini-animate.md) | [Help file](doc/mini-animate.txt) |
| mini.base16 | Base16 colorscheme creation | [README](readmes/mini-base16.md) | [Help file](doc/mini-base16.txt) |
| mini.basics | Common configuration presets | [README](readmes/mini-basics.md) | [Help file](doc/mini-basics.txt) |
| mini.bracketed | Go forward/backward with square brackets | [README](readmes/mini-bracketed.md) | [Help file](doc/mini-bracketed.txt) |
| mini.bufremove | Remove buffers | [README](readmes/mini-bufremove.md) | [Help file](doc/mini-bufremove.txt) |
| mini.clue | Show next key clues | [README](readmes/mini-clue.md) | [Help file](doc/mini-clue.txt) |
| mini.colors | Tweak and save any color scheme | [README](readmes/mini-colors.md) | [Help file](doc/mini-colors.txt) |
| mini.comment | Comment lines | [README](readmes/mini-comment.md) | [Help file](doc/mini-comment.txt) |
| mini.completion | Completion and signature help | [README](readmes/mini-completion.md) | [Help file](doc/mini-completion.txt) |
| mini.cursorword | Autohighlight word under cursor | [README](readmes/mini-cursorword.md) | [Help file](doc/mini-cursorword.txt) |
| mini.deps | Plugin manager | [README](readmes/mini-deps.md) | [Help file](doc/mini-deps.txt) |
| mini.diff | Work with diff hunks | [README](readmes/mini-diff.md) | [Help file](doc/mini-diff.txt) |
| mini.doc | Generate Neovim help files | [README](readmes/mini-doc.md) | [Help file](doc/mini-doc.txt) |
| mini.extra | Extra 'mini.nvim' functionality | [README](readmes/mini-extra.md) | [Help file](doc/mini-extra.txt) |
| mini.files | Navigate and manipulate file system | [README](readmes/mini-files.md) | [Help file](doc/mini-files.txt) |
| mini.fuzzy | Fuzzy matching | [README](readmes/mini-fuzzy.md) | [Help file](doc/mini-fuzzy.txt) |
| mini.git | Git integration | [README](readmes/mini-git.md) | [Help file](doc/mini-git.txt) |
| mini.hipatterns | Highlight patterns in text | [README](readmes/mini-hipatterns.md) | [Help file](doc/mini-hipatterns.txt) |
| mini.hues | Generate configurable color scheme | [README](readmes/mini-hues.md) | [Help file](doc/mini-hues.txt) |
| mini.icons | Icon provider | [README](readmes/mini-icons.md) | [Help file](doc/mini-icons.txt) |
| mini.indentscope | Visualize and work with indent scope | [README](readmes/mini-indentscope.md) | [Help file](doc/mini-indentscope.txt) |
| mini.jump | Jump to next/previous single character | [README](readmes/mini-jump.md) | [Help file](doc/mini-jump.txt) |
| mini.jump2d | Jump within visible lines | [README](readmes/mini-jump2d.md) | [Help file](doc/mini-jump2d.txt) |
| mini.map | Window with buffer text overview | [README](readmes/mini-map.md) | [Help file](doc/mini-map.txt) |
| mini.misc | Miscellaneous functions | [README](readmes/mini-misc.md) | [Help file](doc/mini-misc.txt) |
| mini.move | Move any selection in any direction | [README](readmes/mini-move.md) | [Help file](doc/mini-move.txt) |
| mini.notify | Show notifications | [README](readmes/mini-notify.md) | [Help file](doc/mini-notify.txt) |
| mini.operators | Text edit operators | [README](readmes/mini-operators.md) | [Help file](doc/mini-operators.txt) |
| mini.pairs | Autopairs | [README](readmes/mini-pairs.md) | [Help file](doc/mini-pairs.txt) |
| mini.pick | Pick anything | [README](readmes/mini-pick.md) | [Help file](doc/mini-pick.txt) |
| mini.sessions | Session management | [README](readmes/mini-sessions.md) | [Help file](doc/mini-sessions.txt) |
| mini.splitjoin | Split and join arguments | [README](readmes/mini-splitjoin.md) | [Help file](doc/mini-splitjoin.txt) |
| mini.starter | Start screen | [README](readmes/mini-starter.md) | [Help file](doc/mini-starter.txt) |
| mini.statusline | Statusline | [README](readmes/mini-statusline.md) | [Help file](doc/mini-statusline.txt) |
| mini.surround | Surround actions | [README](readmes/mini-surround.md) | [Help file](doc/mini-surround.txt) |
| mini.tabline | Tabline | [README](readmes/mini-tabline.md) | [Help file](doc/mini-tabline.txt) |
| mini.test | Test Neovim plugins | [README](readmes/mini-test.md) | [Help file](doc/mini-test.txt) |
| mini.trailspace | Trailspace (highlight and remove) | [README](readmes/mini-trailspace.md) | [Help file](doc/mini-trailspace.txt) |
| mini.visits | Track and reuse file system visits | [README](readmes/mini-visits.md) | [Help file](doc/mini-visits.txt) |
## 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 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` section in help file 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` section in main help file for common recipes.
- **Silencing**. Each module providing non-error feedback can be configured to not do that by setting `config.silent = true` (either inside `setup()` call or on the fly).
- **Highlighting**. Appearance of module's output is controlled by certain set of highlight groups (see `:h highlight-groups`). By default they usually link to some semantically close built-in highlight group. Use `:highlight` command or `vim.api.nvim_set_hl()` Lua function to customize highlighting. To see a more calibrated look, use 'mini.hues', 'mini.base16', or plugin's colorscheme.
- **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.
- **Not filetype/language specific**. Including functionality which needs several filetype/language specific implementations is an explicit no-goal of this project. This is mostly due to the potential increase in maintenance to keep implementation up to date. However, any part which might need filetype/language specific tuning should be designed to allow it by letting user set proper buffer options and/or local configuration.
## Plugin colorschemes
This plugin comes with several color schemes (all have both dark and light variants):
- `randomhue` - random background and foreground of the same hue with medium saturation.
- `minicyan` - cyan and grey main colors with medium contrast and saturation palette.
- `minischeme` - blue and yellow main colors with high contrast and saturation palette.
Activate them as regular `colorscheme` (for example, `:colorscheme randomhue` or `:colorscheme minicyan`). You can see how they look in [demo of 'mini.hues'](readmes/mini-hues.md#demo) or [demo of 'mini.base16'](readmes/mini-base16.md#demo).
## Planned modules
This is the list of modules I currently intend to implement eventually (as my free time and dedication will allow), in alphabetical order:
- 'mini.cycle' - cycle through alternatives with pre-defined rules. Something like [monaqa/dial.nvim](https://github.com/monaqa/dial.nvim) and [AndrewRadev/switch.vim](https://github.com/AndrewRadev/switch.vim)
- 'mini.keymap' - utilities to make non-trivial mappings (like [max397574/better-escape.nvim](https://github.com/max397574/better-escape.nvim) and dot-repeatable mappings).
- 'mini.snippets' - work with snippets. Something like [L3MON4D3/LuaSnip](https://github.com/L3MON4D3/LuaSnip) but only with more straightforward functionality.
- 'mini.statuscolumn' - customizable 'statuscolumn'.
- 'mini.terminals' - coherently manage terminal windows and send text from buffers to terminal windows. Something like [kassio/neoterm](https://github.com/kassio/neoterm).
- 'mini.quickfix' - fuzzy search and preview of quickfix entries. Possibly with some presets for populating quickfix list (like files, help tags, etc.). Similar to [kevinhwang91/nvim-bqf](https://github.com/kevinhwang91/nvim-bqf).

View File

@ -0,0 +1,963 @@
# How to test with 'mini.test'
Writing tests for Neovim Lua plugin is hard. Writing good tests for Neovim Lua plugin is even harder. The 'mini.test' module is designed to make it reasonably easier while still allowing lots of flexibility. It deliberately favors a more verbose and program-like style of writing tests, opposite to "human readable, DSL like" approach of [nvim-lua/plenary.nvim](https://github.com/nvim-lua/plenary.nvim) ("busted-style testing" from [Olivine-Labs/busted](https://github.com/Olivine-Labs/busted)). Although the latter is also possible.
This file is intended as a hands-on introduction to 'mini.test' with examples. For more details, see 'mini.test' section of [help file](doc/mini.txt) and tests of this plugin's modules.
General approach of writing test files:
- Organize tests in separate Lua files.
- Each file should be associated with a test set table (output of `MiniTest.new_set()`). Recommended approach is to create it manually in each test file and then return it.
- Each test action should be defined in separate function assign to an entry of test set.
- It is strongly encouraged to use custom Neovim processes to do actual testing inside test action. See [Using child process](#using-child-process).
**NOTES**:
- All commands are assumed to be executed with current working directory being a root of your Neovim plugin project. That is both for shell and Neovim commands.
- All paths are assumed to be relative to current working directory.
## Example plugin
In this file we will be testing 'hello_lines' plugin (once some basic concepts are introduced). It will have functionality to add prefix 'Hello ' to lines implemented in a single file 'lua/hello_lines/init.lua':
<details><summary>'lua/hello_lines/init.lua'</summary>
```lua
local M = {}
--- Prepend 'Hello ' to every element
---@param lines table Array. Default: { 'world' }.
---@return table Array of strings.
M.compute = function(lines)
lines = lines or { 'world' }
return vim.tbl_map(function(x) return 'Hello ' .. tostring(x) end, lines)
end
local ns_id = vim.api.nvim_create_namespace('hello_lines')
--- Set lines with highlighted 'Hello ' prefix
---@param buf_id number Buffer handle where lines should be set. Default: 0.
---@param lines table Array. Default: { 'world' }.
M.set_lines = function(buf_id, lines)
buf_id = buf_id or 0
lines = lines or { 'world' }
vim.api.nvim_buf_set_lines(buf_id or 0, 0, -1, true, M.compute(lines))
for i = 1, #lines do
vim.highlight.range(buf_id, ns_id, 'Special', { i - 1, 0 }, { i - 1, 5 }, {})
end
end
return M
```
</details>
## Quick demo
Here is a quick demo of how tests with 'mini.test' look like:
<details><summary>'tests/test_hello_lines.lua'</summary>
```lua
-- Define helper aliases
local new_set = MiniTest.new_set
local expect, eq = MiniTest.expect, MiniTest.expect.equality
-- Create (but not start) child Neovim object
local child = MiniTest.new_child_neovim()
-- Define main test set of this file
local T = new_set({
-- Register hooks
hooks = {
-- This will be executed before every (even nested) case
pre_case = function()
-- Restart child process with custom 'init.lua' script
child.restart({ '-u', 'scripts/minimal_init.lua' })
-- Load tested plugin
child.lua([[M = require('hello_lines')]])
end,
-- This will be executed one after all tests from this set are finished
post_once = child.stop,
},
})
-- Test set fields define nested structure
T['compute()'] = new_set()
-- Define test action as callable field of test set.
-- If it produces error - test fails.
T['compute()']['works'] = function()
-- Execute Lua code inside child process, get its result and compare with
-- expected result
eq(child.lua_get([[M.compute({'a', 'b'})]]), { 'Hello a', 'Hello b' })
end
T['compute()']['uses correct defaults'] = function()
eq(child.lua_get([[M.compute()]]), { 'Hello world' })
end
-- Make parametrized tests. This will create three copies of each case
T['set_lines()'] = new_set({ parametrize = { {}, { 0, { 'a' } }, { 0, { 1, 2, 3 } } } })
-- Use arguments from test parametrization
T['set_lines()']['works'] = function(buf_id, lines)
-- Directly modify some options to make better test
child.o.lines, child.o.columns = 10, 20
child.bo.readonly = false
-- Execute Lua code without returning value
child.lua('M.set_lines(...)', { buf_id, lines })
-- Test screen state. On first run it will automatically create reference
-- screenshots with text and look information in predefined location. On
-- later runs it will compare current screenshot with reference. Will throw
-- informative error with helpful information if they don't match exactly.
expect.reference_screenshot(child.get_screenshot())
end
-- Return test set which will be collected and execute inside `MiniTest.run()`
return T
```
</details>
## File organization
It might be a bit overwhelming. It actually is for most of the people. However, it should be done once and then you rarely need to touch it.
Overview of full file structure used in for testing 'hello_lines' plugin:
```
.
├── deps
│ └── mini.nvim # Mandatory
├── lua
│ └── hello_lines
│ └── init.lua # Mandatory
├── Makefile # Recommended
├── scripts
│ ├── minimal_init.lua # Mandatory
│ └── minitest.lua # Recommended
└── tests
└── test_hello_lines.lua # Mandatory
```
To write tests, you'll need these files:
Mandatory:
- **Your Lua plugin in 'lua' directory**. Here we will be testing 'hello_lines' plugin.
- **Test files**. By default they should be Lua files located in 'tests/' directory and named with 'test_' prefix. For example, we will write everything in 'test_hello_lines.lua'. It is usually a good idea to follow this template (will be assumed for the rest of this file):
<details><summary>Template for test files</summary>
```lua
local new_set = MiniTest.new_set
local expect, eq = MiniTest.expect, MiniTest.expect.equality
local T = new_set()
-- Actual tests definitions will go here
return T
```
</details><br>
- **'mini.nvim' dependency**. It is needed to use its 'mini.test' module. Proposed way to store it is in 'deps/mini.nvim' directory. Create it with `git`:
```bash
mkdir -p deps
git clone --filter=blob:none https://github.com/echasnovski/mini.nvim deps/mini.nvim
```
- **Manual Neovim startup file** (a.k.a 'init.lua') with proposed path 'scripts/minimal_init.lua'. It will be used to ensure that Neovim processes can recognize your tested plugin and 'mini.nvim' dependency. Proposed minimal content:
<details><summary>'scripts/minimal_init.lua'</summary>
```lua
-- Add current directory to 'runtimepath' to be able to use 'lua' files
vim.cmd([[let &rtp.=','.getcwd()]])
-- Set up 'mini.test' only when calling headless Neovim (like with `make test`)
if #vim.api.nvim_list_uis() == 0 then
-- Add 'mini.nvim' to 'runtimepath' to be able to use 'mini.test'
-- Assumed that 'mini.nvim' is stored in 'deps/mini.nvim'
vim.cmd('set rtp+=deps/mini.nvim')
-- Set up 'mini.test'
require('mini.test').setup()
end
```
</details><br>
Recommended:
- **Makefile**. In order to simplify running tests from shell and inside Continuous Integration services (like Github Actions), it is recommended to define Makefile. It will define steps for running tests. Proposed template:
<details><summary>Template for Makefile</summary>
```
# Run all test files
test: deps/mini.nvim
nvim --headless --noplugin -u ./scripts/minimal_init.lua -c "lua MiniTest.run()"
# Run test from file at `$FILE` environment variable
test_file: deps/mini.nvim
nvim --headless --noplugin -u ./scripts/minimal_init.lua -c "lua MiniTest.run_file('$(FILE)')"
# Download 'mini.nvim' to use its 'mini.test' testing module
deps/mini.nvim:
@mkdir -p deps
git clone --filter=blob:none https://github.com/echasnovski/mini.nvim $@
```
</details><br>
- **'mini.test' script** at 'scripts/minitest.lua'. Use it to customize what is tested (which files, etc.) and how. Usually not needed, but otherwise should have some variant of a call to `MiniTest.run()`.
## Running tests
The 'mini.test' module out of the box supports two major ways of running tests:
- **Interactive**. All test files will be run directly inside current Neovim session. This proved to be very useful for debugging while writing tests. To run tests, simply execute `:lua MiniTest.run()` / `:lua MiniTest.run_file()` / `:lua MiniTest.run_at_location()` (assuming, you already have 'mini.test' set up with `require('mini.test').setup()`). With default configuration this will result into floating window with information about results of test execution. Press `q` to close it. **Note**: Be careful though, as it might affect your current setup. To avoid this, [use child processes](#using-child-process) inside tests.
- **Headless** (from shell). Start headless Neovim process with proper startup file and execute `lua MiniTest.run()`. Assuming full file organization from previous section, this can be achieved with `make test`. This will show information about results of test execution directly in shell.
## Basics
These sections will show some basic capabilities of 'mini.test' and how to use them. In all examples code blocks represent some whole test file (like 'tests/test_basics.lua').
### First test
A test is defined as function assigned to a field of test set. If it throws error, test has failed. Test file should return single test set. Here is an example:
```lua
local T = MiniTest.new_set()
T['works'] = function()
local x = 1 + 1
if x ~= 2 then
error('`x` is not equal to 2')
end
end
return T
```
Writing `if .. error() .. end` is too tiresome. That is why 'mini.test' comes with very minimal but usually quite enough set of *expectations*: `MiniTest.expect`. They display the intended expectation between objects and will throw error with informative message if it doesn't hold. Here is a rewritten previous example:
```lua
local T = MiniTest.new_set()
T['works'] = function()
local x = 1 + 1
MiniTest.expect.equality(x, 2)
end
return T
```
Test sets can be nested. This will be useful in combination with [hooks](#hooks) and [parametrization](#test-parametrization):
```lua
local T = MiniTest.new_set()
T['big scope'] = new_set()
T['big scope']['works'] = function()
local x = 1 + 1
MiniTest.expect.equality(x, 2)
end
T['big scope']['also works'] = function()
local x = 2 + 2
MiniTest.expect.equality(x, 4)
end
T['out of scope'] = function()
local x = 3 + 3
MiniTest.expect.equality(x, 6)
end
return T
```
**NOTE**: 'mini.test' supports emulation of busted-style testing by default. So previous example can be written like this:
```lua
describe('big scope', function()
it('works', function()
local x = 1 + 1
MiniTest.expect.equality(x, 2)
end)
it('also works', function()
local x = 2 + 2
MiniTest.expect.equality(x, 4)
end)
end)
it('out of scope', function()
local x = 3 + 3
MiniTest.expect.equality(x, 6)
end)
-- NOTE: when using this style, no test set should be returned
```
Although this is possible, the rest of this file will use a recommended test set approach.
### Builtin expectations
These four builtin expectations are the ones used most commonly:
```lua
local T = MiniTest.new_set()
local expect, eq = MiniTest.expect, MiniTest.expect.equality
local x = 1 + 1
-- This is so frequently used that having short alias proved useful
T['expect.equality'] = function()
eq(x, 2)
end
T['expect.no_equality'] = function()
expect.no_equality(x, 1)
end
T['expect.error'] = function()
-- This expectation will pass because function will throw an error
expect.error(function()
if x == 2 then error('Deliberate error') end
end)
end
T['expect.no_error'] = function()
-- This expectation will pass because function will *not* throw an error
expect.no_error(function()
if x ~= 2 then error('This should not be thrown') end
end)
end
return T
```
### Writing custom expectation
Although you can use `if ... error() ... end` approach, there is `MiniTest.new_expectation()` to simplify this process for some repetitive expectation. Here is an example used in this plugin:
```lua
local T = MiniTest.new_set()
local expect_match = MiniTest.new_expectation(
-- Expectation subject
'string matching',
-- Predicate
function(str, pattern) return str:find(pattern) ~= nil end,
-- Fail context
function(str, pattern)
return string.format('Pattern: %s\nObserved string: %s', vim.inspect(pattern), str)
end
)
T['string matching'] = function()
local x = 'abcd'
-- This will pass
expect_match(x, '^a')
-- This will fail
expect_match(x, 'x')
end
return T
```
Executing this content from file 'tests/test_basics.lua' will fail with the following message:
```
FAIL in "tests/test_basics.lua | string matching":
Failed expectation for string matching.
Pattern: "x"
Observed string: abcd
Traceback:
tests/test_basics.lua:20
```
### Hooks
Hooks are functions that will be called without arguments at predefined stages of test execution. They are defined for a test set. There are four types of hooks:
- **pre_once** - executed before first (filtered) node.
- **pre_case** - executed before each case (even nested).
- **post_case** - executed after each case (even nested).
- **post_once** - executed after last (filtered) node.
Example:
```lua
local new_set = MiniTest.new_set
local expect, eq = MiniTest.expect, MiniTest.expect.equality
local T = new_set()
local n = 0
local increase_n = function() n = n + 1 end
T['hooks'] = new_set({
hooks = { pre_once = increase_n, pre_case = increase_n, post_case = increase_n, post_once = increase_n },
})
T['hooks']['work'] = function()
-- `n` will be increased twice: in `pre_once` and `pre_case`
eq(n, 2)
end
T['hooks']['work again'] = function()
-- `n` will be increased twice: in `post_case` from previous case and
-- `pre_case` before this one
eq(n, 4)
end
T['after hooks set'] = function()
-- `n` will be again increased twice: in `post_case` from previous case and
-- `post_once` after last case in T['hooks'] test set
eq(n, 6)
end
return T
```
### Test parametrization
One of the distinctive features of 'mini.test' is ability to leverage test parametrization. As hooks, it is a feature of test set.
Example of simple parametrization:
```lua
local new_set = MiniTest.new_set
local eq = MiniTest.expect.equality
local T = new_set()
-- Each parameter should be an array to allow parametrizing multiple arguments
T['parametrize'] = new_set({ parametrize = { { 1 }, { 2 } } })
-- This will result into two cases. First will fail.
T['parametrize']['works'] = function(x)
eq(x, 2)
end
-- Parametrization can be nested. Cases are "multiplied" with every combination
-- of parameters.
T['parametrize']['nested'] = new_set({ parametrize = { { '1' }, { '2' } } })
-- This will result into four cases. Two of them will fail.
T['parametrize']['nested']['works'] = function(x, y)
eq(tostring(x), y)
end
-- Parametrizing multiple arguments
T['parametrize multiple arguments'] = new_set({ parametrize = { { 1, 1 }, { 2, 2 } } })
-- This will result into two cases. Both will pass.
T['parametrize multiple arguments']['works'] = function(x, y)
eq(x, y)
end
return T
```
### Runtime access to current cases
There is `MiniTest.current` table containing information about "current" test cases. It has `all_cases` and `case` fields with all currently executed tests and *the* current case.
Test case is a single unit of sequential test execution. It contains all information needed to execute test case along with data about its execution. Example:
```lua
local new_set = MiniTest.new_set
local eq = MiniTest.expect.equality
local T = new_set()
T['MiniTest.current.all_cases'] = function()
-- A useful hack: show runtime data with expecting it to be something else
eq(MiniTest.current.all_cases, 0)
end
T['MiniTest.current.case'] = function()
eq(MiniTest.current.case, 0)
end
return T
```
This will result into following lengthy fails:
<details><summary>Fail information</summary>
```
FAIL in "tests/test_basics.lua | MiniTest.current.all_cases":
Failed expectation for equality.
Left: { {
args = {},
data = {},
desc = { "tests/test_basics.lua", "MiniTest.current.all_cases" },
exec = {
fails = {},
notes = {},
state = "Executing test"
},
hooks = {
post = {},
pre = {}
},
test = <function 1>
}, {
args = {},
data = {},
desc = { "tests/test_basics.lua", "MiniTest.current.case" },
hooks = {
post = {},
pre = {}
},
test = <function 2>
} }
Right: 0
Traceback:
tests/test_basics.lua:8
FAIL in "tests/test_basics.lua | MiniTest.current.case":
Failed expectation for equality.
Left: {
args = {},
data = {},
desc = { "tests/test_basics.lua", "MiniTest.current.case" },
exec = {
fails = {},
notes = {},
state = "Executing test"
},
hooks = {
post = {},
pre = {}
},
test = <function 1>
}
Right: 0
Traceback:
tests/test_basics.lua:12
```
</details>
### Case helpers
There are some functions intended to help writing more robust cases: `skip()`, `finally()`, and `add_note()`. The `MiniTest.current` table contains useful information about the current state of tests execution.
Example:
```lua
local T = MiniTest.new_set()
-- `MiniTest.skip()` allows skipping rest of test execution while giving an
-- informative note. This test will pass with notes.
T['skip()'] = function()
if 1 + 1 == 2 then
MiniTest.skip('Apparently, 1 + 1 is 2')
end
error('1 + 1 is not 2')
end
-- `MiniTest.add_note()` allows adding notes. Final state will have
-- "with notes" suffix.
T['add_note()'] = function()
MiniTest.add_note('This test is not important.')
error('Custom error.')
end
-- `MiniTest.finally()` allows registering some function to be executed after
-- this case is finished executing (with or without an error).
T['finally()'] = function()
-- Add note only if test fails
MiniTest.finally(function()
if #MiniTest.current.case.exec.fails > 0 then
MiniTest.add_note('This test is flaky.')
end
end)
error('Expected error from time to time')
end
return T
```
This will result into following messages:
```
NOTE in "tests/test_basics.lua | skip()": Apparently, 1 + 1 is 2
FAIL in "tests/test_basics.lua | add_note()": tests/test_basics.lua:16: Custom error.
NOTE in "tests/test_basics.lua | add_note()": This test is not important.
FAIL in "tests/test_basics.lua | finally()": tests/test_basics.lua:28: Expected error from time to time
NOTE in "tests/test_basics.lua | finally()": This test is flaky.
```
## Customizing test run
Test run consists from two stages:
- **Collection**. It will source each appropriate file (customizable), combine all test sets into single test set, convert it from hierarchical to sequential form (array of test cases), and filter cases based on customizable predicate.
- **Execution**. It will safely execute array of test cases (with each pre-hooks, test action, post-hooks) one after another in scheduled asynchronous fashion while collecting information about how it went and calling customizable reporter methods.
All configuration goes into `opts` argument of `MiniTest.run()`.
### Collection: custom files and filter
You can customize which files will be sourced and which cases will be later executed. Example:
```lua
local new_set = MiniTest.new_set
local T = new_set()
-- Use `data` field to pass custom information for easier test management
T['fast'] = new_set({ data = { type = 'fast' } })
T['fast']['first test'] = function() end
T['fast']['second test'] = function() end
T['slow'] = new_set({ data = { type = 'slow' } })
T['slow']['first test'] = function() vim.loop.sleep(1000) end
T['slow']['second test'] = function() vim.loop.sleep(1000) end
return T
```
You can run only this file ('tests/test_basics.lua') and only "fast" cases with this call:
```lua
MiniTest.run({
collect = {
find_files = function() return { 'tests/test_basics.lua' } end,
filter_cases = function(case) return case.data.type == 'fast' end,
}
})
```
### Execution: custom reporter and stop on first error
You can customize execution of test cases with custom reporter (how test results are displayed in real time) and whether to stop execution after the first test case fail/error. Execution doesn't result into any output, instead it updates `MiniTest.current.all_cases` in place: each case gets an `exec` field with information about how its execution went.
Example of showing status summary table in the command line after everything is finished:
```lua
local reporter = {
-- Other used methods are `start(cases)` and `update(case_num)`
finish = function()
local summary = {}
for _, c in ipairs(MiniTest.current.all_cases) do
local state = c.exec.state
summary[state] = (summary[state] or 0) + 1
end
print(vim.inspect(summary, { newline = ' ', indent = '' }))
end,
}
MiniTest.run({ execute = { reporter = reporter } })
```
## Using child process
Main feature of 'mini.test' which makes it different from other Lua testing frameworks is its design towards **custom usage of child Neovim process inside tests**. Ultimately, each test should be done with fresh Neovim process initialized with bare minimum setup (like allowing to load your plugin). To make this easier, there is a dedicated function `MiniTest.new_child_neovim()`. It returns an object with many useful helper methods, like for start/stop/restart, redirected execution (write code in current process, it gets executed in child one), emulating typing keys, **testing screen state**, etc.
### Start/stop/restart
You can start/stop/restart child process associated with this child Neovim object. Current (from which testing is initiated) and child Neovim processes can "talk" to each through RPC messages (see `:h RPC`). It means you can programmatically execute code inside child process, get its output inside current process, and test if it meets your expectation. Child process is headless but fully functioning process which allows you to test things such as extmarks, floating windows, etc.
Although this approach proved to be useful and efficient, it is not ideal. Here are some limitations:
- Due to current RPC protocol implementation functions and userdata can't be used in both input and output with child process. Indicator of this issue is a `Cannot convert given lua type` error. Usual solution is to move some logic on the side of child process, like create and use global functions (note that they will be "forgotten" after next restart).
- Sometimes hanging process will occur: it stops executing without any output. Most of the time it is because Neovim process is "blocked", i.e. it waits for user input and won't return from other call. Common causes are active hit-enter-prompt (solution: increase prompt height to a bigger value) or Operator-pending mode (solution: exit it). To mitigate this experience, most helper methods will throw an error if they can deduce that immediate execution will lead to hanging state.
Here is recommended setup for managing child processes. It will make fresh Neovim process before every test case:
```lua
local child = MiniTest.new_child_neovim()
local T = MiniTest.new_set({
hooks = {
pre_case = function()
-- Restart child process with custom 'init.lua' script
child.restart({ '-u', 'scripts/minimal_init.lua' })
-- Load tested plugin
child.lua([[M = require('hello_lines')]])
end,
-- Stop once all test cases are finished
post_once = child.stop,
},
})
-- Define some tests here
return T
```
### Executing Lua code
Previous section already demonstrated that there is a `child.lua()` method. It will execute arbitrary Lua code in the form of a single string. This is basically a wrapper for `vim.api.nvim_exec_lua()`. There is also a convenience wrapper `child.lua_get()` which is essentially a `child.lua('return ' .. s, ...)`. Examples:
```lua
local eq = MiniTest.expect.equality
local child = MiniTest.new_child_neovim()
local T = MiniTest.new_set({
hooks = {
pre_case = function()
child.restart({ '-u', 'scripts/minimal_init.lua' })
child.lua([[M = require('hello_lines')]])
end,
post_once = child.stop,
},
})
T['lua()'] = MiniTest.new_set()
T['lua()']['works'] = function()
child.lua('_G.n = 0; _G.n = _G.n + 1')
eq(child.lua('return _G.n'), 1)
end
T['lua()']['can use tested plugin'] = function()
eq(child.lua('return M.compute()'), { 'Hello world' })
eq(child.lua([[return M.compute({'a', 'b'})]]), { 'Hello a', 'Hello b' })
end
T['lua_get()'] = function()
child.lua('_G.n = 0')
eq(child.lua_get('_G.n'), child.lua('return _G.n'))
end
return T
```
### Managing Neovim options and state
Although ability to execute arbitrary Lua code is technically enough to write any tests, it gets cumbersome very quickly due it using only string input. That is why there are many convenience helpers with the same idea: write code inside current Neovim process that will be automatically executed same way in child process. Here is the showcase:
```lua
local new_set = MiniTest.new_set
local eq = MiniTest.expect.equality
local child = MiniTest.new_child_neovim()
local T = MiniTest.new_set({
hooks = {
pre_case = function()
child.restart({ '-u', 'scripts/minimal_init.lua' })
child.lua([[M = require('hello_lines')]])
end,
post_once = child.stop,
},
})
-- These methods will "redirect" execution to child through `vim.rpcrequest()`
-- and `vim.rpcnotify()` respectively. Any call `child.api.xxx(...)` returns
-- the output of `vim.api.xxx(...)` executed inside child process.
T['api()/api_notify()'] = function()
-- Set option. For some reason, first buffer is 'readonly' which leads to
-- high delay in test execution
child.api.nvim_set_option_value('readonly', false, { buf = 0 })
-- Set all lines
child.api.nvim_buf_set_lines(0, 0, -1, true, { 'aaa' })
-- Get all lines and test with expected ones
eq(child.api.nvim_buf_get_lines(0, 0, -1, true), { 'aaa' })
end
-- Execute Vimscript with or without capturing its output
T['cmd()/cmd()'] = function()
child.cmd('hi Comment guifg=#aaaaaa')
eq(child.cmd_capture('hi Comment'), 'Comment xxx guifg=#aaaaaa')
end
-- There are redirection tables for most of the main Neovim functionality
T['various redirection tables with methods'] = function()
eq(child.fn.fnamemodify('hello_lines.lua', ':t:r'), 'hello_lines')
eq(child.loop.hrtime() > 0, true)
eq(child.lsp.get_clients(), {})
-- And more
end
-- There are redirection tables for scoped (buffer, window, etc.) variables
-- You can use them to both set and get values
T['redirection tables for variables'] = function()
child.b.aaa = true
eq(child.b.aaa, true)
eq(child.b.aaa, child.lua_get('vim.b.aaa'))
end
-- There are redirection tables for scoped (buffer, window, etc.) options
-- You can use them to both set and get values
T['redirection tables for options'] = function()
child.o.lines, child.o.columns = 5, 12
eq(child.o.lines, 5)
eq({ child.o.lines, child.o.columns }, child.lua_get('{ vim.o.lines, vim.o.columns }'))
end
return T
```
### Emulate typing keys
Very important part of testing is emulating user typing keys. There is a special `child.type_keys()` helper method for that. Examples:
```lua
local eq = MiniTest.expect.equality
local child = MiniTest.new_child_neovim()
local T = MiniTest.new_set({
hooks = {
pre_case = function()
child.restart({ '-u', 'scripts/minimal_init.lua' })
child.bo.readonly = false
child.lua([[M = require('hello_lines')]])
end,
post_once = child.stop,
},
})
local get_lines = function() return child.api.nvim_buf_get_lines(0, 0, -1, true) end
T['type_keys()'] = MiniTest.new_set()
T['type_keys()']['works'] = function()
-- It can take one string
child.type_keys('iabcde<Esc>')
eq(get_lines(), { 'abcde' })
eq(child.fn.mode(), 'n')
-- Or several strings which improves readability
child.type_keys('cc', 'fghij', '<Esc>')
eq(get_lines(), { 'fghij' })
-- Or tables of strings (possibly nested)
child.type_keys({ 'cc', { 'j', 'k', 'l', 'm', 'n' } })
eq(get_lines(), { 'jklmn' })
end
T['type_keys()']['allows custom delay'] = function()
-- This adds delay of 500 ms after each supplied string (three times here)
child.type_keys(500, 'i', 'abcde', '<Esc>')
eq(get_lines(), { 'abcde' })
end
return T
```
### Test screen state with screenshots
One of the main difficulties in testing Neovim plugins is verifying that something is actually displayed in the way you intend. Like general highlighting, statusline, tabline, sign column, extmarks, etc. Testing screen state with screenshots makes this a lot easier. There is a `child.get_screenshot()` method which basically calls `screenstring()` (`:h screenstring()`) and `screenattr()` (`:h screenattr()`) for every visible cell (row from 1 to 'lines' option, column from 1 to 'columns' option). It then returns screenshot with two layers:
- <text> - "2d array" (row-column) of single characters displayed at particular cells.
- <attr> - "2d array" (row-column) of symbols representing how text is displayed (basically, "coded" appearance/highlighting). They should be used only in relation to each other: same/different symbols for two cells mean same/different visual appearance. Note: there will be false positives if there are more than 94 different attribute values. To make output more portable and visually useful, outputs of `screenattr()` are coded with single character symbols.
Couple of caveats:
- As is apparent from use of `screenattr()`, these screenshots **can't tell how exactly cell is highlighted**, only **if two cells are highlighted the same**. This is due to the currently lacking functionality in Neovim itself. This might change in the future.
To help manage testing screen state, there is a special `MiniTest.expect.reference_screenshot(screenshot, path, opts)` method. It takes screenshot table along with optional path of where to save this screenshot (if not supplied, inferred from test case description and put in 'tests/screenshots' directory). On first run it will automatically create reference screenshot at `path`. On later runs it will compare current screenshot with reference. Will throw informative error with helpful information if they don't match exactly.
Example:
```lua
local expect = MiniTest.expect
local child = MiniTest.new_child_neovim()
local T = MiniTest.new_set({
hooks = {
pre_case = function()
child.restart({ '-u', 'scripts/minimal_init.lua' })
child.bo.readonly = false
child.lua([[M = require('hello_lines')]])
end,
post_once = child.stop,
},
})
T['set_lines()'] = MiniTest.new_set({ parametrize = { {}, { 0, { 'a' } }, { 0, { 1, 2, 3 } } } })
T['set_lines()']['works'] = function(buf_id, lines)
child.o.lines, child.o.columns = 10, 15
child.lua('M.set_lines(...)', { buf_id, lines })
expect.reference_screenshot(child.get_screenshot())
end
return T
```
This will result into three files in 'tests/screenshots' with names containing test case description along with supplied arguments. Here is example reference screenshot for `{ 0, { 1, 2, 3 } }` arguments (line numbers and ruler for columns is added as file specification to make it easier to find differences between two screenshots):
```
--|---------|-----
01|Hello 1
02|Hello 2
03|Hello 3
04|~
05|~
06|~
07|~
08|~
09|<e] [+] 1,1 All
10|
--|---------|-----
01|000001111111111
02|000001111111111
03|000001111111111
04|222222222222222
05|222222222222222
06|222222222222222
07|222222222222222
08|222222222222222
09|333333333333333
10|444444444444444
```
To update already existing screenshot either delete the corresponding screenshot file and rerun test case or temporarily add `{ force = true }` option to `reference_screenshot()` to force updating the screenshot file.
## General tips
- Create a 'tests/helpers.lua' file with code that can be useful in multiple files. It can have "monkey-patched" versions of 'mini.test' functions. Example:
```lua
local Helpers = {}
Helpers.new_child_neovim = function()
local child = MiniTest.new_child_neovim()
child.setup = function()
child.restart({'-u', 'scripts/minimal_init.lua'})
child.bo.readonly = false
child.lua([[M = require('hello_lines')]])
end
return child
end
return Helpers
```
- Write aliases for commonly used functions at top of the file. It will make your life a little bit easier and usually will lead to more readable tests. Example:
```lua
-- Some code setting up `child`
local set_lines = function(lines) child.api.nvim_buf_set_lines(0, 0, -1, true, lines) end
```

View File

@ -0,0 +1 @@
startup-times.csv

View File

@ -0,0 +1,35 @@
# Benchmarks for 'mini.starter'
This directory contains code and results of benchmarking 'mini.starter' and its alternatives. Target benchmarked value is a total startup time using configuration file (with `-u <init-file>`) corresponding to benchmarked setup. Configuration files are created in three different groups:
- 'init_starter-default.lua' and 'init_empty.lua' represent default 'mini.starter' setup and corresponding 'init.lua' without 'mini.starter'.
- 'init_startify-starter', 'init_startify-original', and 'init_startify-alpha' have comparable output imitating default 'vim-startify' with empty header.
- 'init_dashboard-starter', 'init_dashboard-original', and 'init_dashboard-alpha' have comparable output imitating default 'dashboard-nvim' enabled keybindings.
Summary of startup-times for various 'init' files from 'init-files/' directory can be seen in 'startup-summary.md'. Current benchmark was done with Neovim 0.5.1 on Ubuntu 18.04 (i3-6100). Exact states of plugins used:
- [echasnovski/mini.nvim](https://github.com/echasnovski/mini.nvim/tree/cfa108eeaead1abd8854a1f1cfb02e72482641ce)
- [mhinz/vim-startify](https://github.com/mhinz/vim-startify/tree/81e36c352a8deea54df5ec1e2f4348685569bed2)
- [glepnir/dashboard-nvim](https://github.com/glepnir/dashboard-nvim/tree/ba98ab86487b8eda3b0934b5423759944b5f7ebd)
- [goolord/alpha-nvim](https://github.com/goolord/alpha-nvim/tree/7a49086bf9197f573b396d4ac46262c02dfb9aec)
To rerun locally execute these commands (preferably without anything else running in the background and monitor always on):
```bash
chmod +x install.sh
./install.sh
# This will create file 'startup-times.csv' and update 'startup-summary.md'
# WARNING: this will lead to screen flicker
chmod +x benchmark.sh
./benchmark.sh
```
Structure:
- 'init-files/' - directory with all configuration files being benchmarked. NOTE: all of them contain auto-closing command at the end (`defer_fn(...)`) to most accurately measure startup time. To view its output, remove this command.
- 'benchmark.sh' - script for performing benchmark which is as close to real-world usage as reasonably possible and computing its summary. Its outputs are 'startup-times.csv' and 'startup-summary.md'. All configuration files are benchmarked in alternate fashion: first 'init' file, second, ..., last, first, etc. WARNING: EXECUTION OF THIS SCRIPT LEADS TO MONITOR FLICKERING WHICH MAY CAUSE HARM TO YOUR HEALTH. This is needed to ensure that Neovim was actually opened and something was drawn.
- 'install.sh' - script for installing all required plugins. NOTE: run `chmod +x install.sh` to make it executable.
- 'make_summary.py' - Python script to compute summary statistics of csv-file.
- 'startup-times.csv' (ignored by Git, latest one can be seen in [this gist](https://gist.github.com/echasnovski/85c334396df6fd0cea7bb42246efb97b)) - csv-file with measured startup times. Each row represent single startup round: when all 'init' files are run alternately. Each column represents startup times of single 'init' file.
- 'startup-summary.md' - markdown file as output of 'make_summary.py'. Contains summaries of 'startup-times.csv'.

View File

@ -0,0 +1,56 @@
#!/nix/store/4bj2kxdm1462fzcc2i2s4dn33g2angcc-bash-5.2p32/bin/bash
# Perform benchmarking of startup times with different Neovim 'init' files.
# Execute `nvim -u <*> --startuptime <*>` several times (as closely to actual
# usage as possible) in rounds alternating between input 'init' files (to "mix"
# possible random noise). Store output in .csv file with rows containing
# startup times for a single round, columns - for a single 'init' file.
# WARNING: EXECUTION OF THIS SCRIPT LEADS TO FLICKERING OF SCREEN WHICH WHICH
# MAY CAUSE HARM TO YOUR HEALTH. This is because every 'init' file leads to an
# actual opening of Neovim with later automatic closing.
# Number of rounds to perform benchmark
n_rounds=1000
# Path to output .csv file with startup times per round
csv_file=startup-times.csv
# Path to output .md file with summary table
summary_file=startup-summary.md
# 'Init' files ids with actual paths computed as 'init-files/init_*.lua'
init_files=(starter-default empty startify-starter startify-original startify-alpha dashboard-starter dashboard-original dashboard-alpha)
function comma_join { local IFS=","; shift; echo "$*"; }
function benchmark {
rm -f "$csv_file"
touch "$csv_file"
local tmp_bench_file="tmp-bench.txt"
touch "$tmp_bench_file"
comma_join -- "$@" >> startup-times.csv
for i in $(seq 1 $n_rounds); do
echo "Round $i"
local bench_times=()
for init_file in "$@"; do
nvim -u "init-files/init_$init_file.lua" --startuptime "$tmp_bench_file"
local b_time=$(tail -n 1 "$tmp_bench_file" | cut -d " " -f1)
bench_times=("${bench_times[@]}" "$b_time")
done
comma_join -- "${bench_times[@]}" >> "$csv_file"
rm "$tmp_bench_file"
done
}
benchmark "${init_files[@]}"
# Produce output summary
./make_summary.py "${csv_file}" "${summary_file}"

View File

@ -0,0 +1,21 @@
vim.cmd([[set packpath=/tmp/nvim/site]])
vim.cmd([[packadd alpha-nvim]])
local alpha = require('alpha')
local dashboard = require('alpha.themes.dashboard')
alpha.setup(dashboard.opts)
vim.g.mapleader = ' '
-- Some of these keybindings doesn't exactly match with what is shown in
-- buffer, but this doesn't really matter. Main point is that some keybindings
-- should be pre-made.
vim.api.nvim_set_keymap('n', '<Leader>ff', ':Telescope find_files<CR>', { noremap = true, silent = true })
vim.api.nvim_set_keymap('n', '<Leader>fh', ':Telescope oldfiles<CR>', { noremap = true, silent = true })
vim.api.nvim_set_keymap('n', '<Leader>fr', ':Telescope jumplist<CR>', { noremap = true, silent = true })
vim.api.nvim_set_keymap('n', '<Leader>fg', ':Telescope live_grep<CR>', { noremap = true, silent = true })
vim.api.nvim_set_keymap('n', '<Leader>fm', ':Telescope marks<CR>', { noremap = true, silent = true })
vim.api.nvim_set_keymap('n', '<Leader>sl', ':Telescope command_history<CR>', { noremap = true, silent = true })
-- Close Neovim just after fully opening it. Randomize to make "more real".
vim.defer_fn(function() vim.cmd([[quit]]) end, 100 + 200 * math.random())

View File

@ -0,0 +1,17 @@
vim.cmd([[set packpath=/tmp/nvim/site]])
vim.cmd([[packadd dashboard-nvim]])
vim.g.mapleader = ' '
vim.g.dashboard_default_executive = 'telescope'
vim.api.nvim_set_keymap('n', '<Leader>ss', ':<C-u>SessionSave<CR>', {})
vim.api.nvim_set_keymap('n', '<Leader>sl', ':<C-u>SessionLoad<CR>', {})
vim.api.nvim_set_keymap('n', '<Leader>fh', ':DashboardFindHistory<CR>', { noremap = true, silent = true })
vim.api.nvim_set_keymap('n', '<Leader>ff', ':DashboardFindFile<CR>', { noremap = true, silent = true })
vim.api.nvim_set_keymap('n', '<Leader>tc', ':DashboardChangeColorscheme<CR>', { noremap = true, silent = true })
vim.api.nvim_set_keymap('n', '<Leader>fa', ':DashboardFindWord<CR>', { noremap = true, silent = true })
vim.api.nvim_set_keymap('n', '<Leader>fb', ':DashboardJumpMark<CR>', { noremap = true, silent = true })
vim.api.nvim_set_keymap('n', '<Leader>cn', ':DashboardNewFile<CR>', { noremap = true, silent = true })
-- Close Neovim just after fully opening it. Randomize to make "more real".
vim.defer_fn(function() vim.cmd([[quit]]) end, 100 + 200 * math.random())

View File

@ -0,0 +1,18 @@
vim.cmd([[set packpath=/tmp/nvim/site]])
vim.cmd([[packadd mini.nvim]])
local starter = require('mini.starter')
starter.setup({
items = {
{ name = 'Edit file', action = [[enew]], section = 'Actions' },
{ name = 'Quit', action = [[quit]], section = 'Actions' },
starter.sections.telescope(),
},
content_hooks = {
starter.gen_hook.adding_bullet(),
starter.gen_hook.aligning('center', 'center'),
},
})
-- Close Neovim just after fully opening it. Randomize to make "more real".
vim.defer_fn(function() vim.cmd([[quit]]) end, 100 + 200 * math.random())

View File

@ -0,0 +1,4 @@
vim.cmd([[set packpath=/tmp/nvim/site]])
-- Close Neovim just after fully opening it. Randomize to make "more real".
vim.defer_fn(function() vim.cmd([[quit]]) end, 100 + 200 * math.random())

View File

@ -0,0 +1,7 @@
vim.cmd([[set packpath=/tmp/nvim/site]])
vim.cmd([[packadd mini.nvim]])
require('mini.starter').setup()
-- Close Neovim just after fully opening it. Randomize to make "more real".
vim.defer_fn(function() vim.cmd([[quit]]) end, 100 + 200 * math.random())

View File

@ -0,0 +1,10 @@
vim.cmd([[set packpath=/tmp/nvim/site]])
vim.cmd([[packadd alpha-nvim]])
local alpha = require('alpha')
local startify = require('alpha.themes.startify')
startify.nvim_web_devicons.enabled = false
alpha.setup(startify.opts)
-- Close Neovim just after fully opening it. Randomize to make "more real".
vim.defer_fn(function() vim.cmd([[quit]]) end, 100 + 200 * math.random())

View File

@ -0,0 +1,7 @@
vim.cmd([[set packpath=/tmp/nvim/site]])
vim.cmd([[packadd vim-startify]])
vim.g.startify_custom_header = ''
-- Close Neovim just after fully opening it. Randomize to make "more real".
vim.defer_fn(function() vim.cmd([[quit]]) end, 100 + 200 * math.random())

View File

@ -0,0 +1,20 @@
vim.cmd([[set packpath=/tmp/nvim/site]])
vim.cmd([[packadd mini.nvim]])
local starter = require('mini.starter')
starter.setup({
evaluate_single = true,
items = {
starter.sections.builtin_actions(),
starter.sections.recent_files(10, false),
starter.sections.recent_files(10, true),
},
content_hooks = {
starter.gen_hook.adding_bullet(),
starter.gen_hook.indexing('all', { 'Builtin actions' }),
starter.gen_hook.padding(3, 2),
},
})
-- Close Neovim just after fully opening it. Randomize to make "more real".
vim.defer_fn(function() vim.cmd([[quit]]) end, 100 + 200 * math.random())

View File

@ -0,0 +1,10 @@
#!/nix/store/4bj2kxdm1462fzcc2i2s4dn33g2angcc-bash-5.2p32/bin/bash
PLUGINPATH=/tmp/nvim/site/pack/bench/opt
rm -rf $PLUGINPATH
mkdir -p $PLUGINPATH
cd $PLUGINPATH
git clone --depth 1 https://github.com/echasnovski/mini.nvim
git clone --depth 1 https://github.com/goolord/alpha-nvim
git clone --depth 1 https://github.com/glepnir/dashboard-nvim
git clone --depth 1 https://github.com/mhinz/vim-startify

View File

@ -0,0 +1,68 @@
#!/usr/bin/env python
import argparse
import csv
import statistics
def read_csv_columns(csv_path):
with open(csv_path, "r") as csvfile:
reader = csv.DictReader(csvfile)
res = {h: [] for h in reader.fieldnames}
for line_dict in reader:
for h, val in line_dict.items():
res[h].append(float(val))
return res
def summarise_array(x):
return {
"median": statistics.median(x),
"mean": statistics.mean(x),
"stdev": statistics.stdev(x),
"minimum": min(x),
"maximum": max(x),
}
def save_md_summary(summary, output_path):
lines = []
row_names = list(summary.keys())
col_names = ["init file"] + list(summary[row_names[0]].keys())
lines.append(" | ".join(col_names))
lines.append(" | ".join("---" for _ in col_names))
for row_n in row_names:
l = [row_n] + [str(round(x, 1)) + 'ms' for x in summary[row_n].values()]
lines.append(" | ".join(l))
lines = ["| " + l + " |\n" for l in lines]
with open(output_path, "w") as output:
for l in lines:
output.write(l)
def compute_summary(csv_path):
columns = read_csv_columns(csv_path)
return {h: summarise_array(x) for h, x in columns.items()}
def main():
parser = argparse.ArgumentParser()
parser.add_argument(
"input_csv", help="path to file with startup times in csv format", type=str
)
parser.add_argument(
"output_md",
help="output path where markdown summary table will be written",
type=str,
)
args = parser.parse_args()
save_md_summary(compute_summary(args.input_csv), args.output_md)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,10 @@
| init file | median | mean | stdev | minimum | maximum |
| --- | --- | --- | --- | --- | --- |
| starter-default | 69.0ms | 70.8ms | 4.3ms | 63.6ms | 82.7ms |
| empty | 64.1ms | 65.8ms | 4.3ms | 60.0ms | 79.4ms |
| startify-starter | 70.2ms | 71.9ms | 4.5ms | 64.3ms | 85.7ms |
| startify-original | 80.3ms | 82.2ms | 4.5ms | 76.4ms | 107.2ms |
| startify-alpha | 72.1ms | 73.8ms | 4.3ms | 66.5ms | 86.9ms |
| dashboard-starter | 68.4ms | 70.3ms | 4.5ms | 63.3ms | 102.9ms |
| dashboard-original | 70.6ms | 72.3ms | 4.4ms | 65.5ms | 99.6ms |
| dashboard-alpha | 69.8ms | 71.5ms | 4.4ms | 64.7ms | 97.2ms |

View File

@ -0,0 +1,95 @@
-- 'Minicyan' color scheme
-- Derived from base16 (https://github.com/chriskempson/base16) and
-- mini_palette palette generator
local use_cterm, palette
-- Dark palette is an output of 'MiniBase16.mini_palette':
-- - Background '#0A2A2A' (LCh(uv) = 15-10-192)
-- - Foreground '#D0D0D0' (Lch(uv) = 83-0-0)
-- - Accent chroma 50
if vim.o.background == 'dark' then
palette = {
base00 = '#0a2a2a',
base01 = '#324747',
base02 = '#556868',
base03 = '#788a8a',
base04 = '#bbbbbb',
base05 = '#d0d0d0',
base06 = '#e6e6e6',
base07 = '#fcfcfc',
base08 = '#ebcd91',
base09 = '#9f8340',
base0A = '#209870',
base0B = '#82e3ba',
base0C = '#bb6d9b',
base0D = '#a9d4ff',
base0E = '#ffb9e5',
base0F = '#598ab9',
}
use_cterm = {
base00 = 235,
base01 = 238,
base02 = 241,
base03 = 102,
base04 = 250,
base05 = 252,
base06 = 254,
base07 = 231,
base08 = 186,
base09 = 136,
base0A = 29,
base0B = 115,
base0C = 132,
base0D = 153,
base0E = 218,
base0F = 67,
}
end
-- Light palette is an 'inverted dark', output of 'MiniBase16.mini_palette':
-- - Background '#C0D2D2' (LCh(uv) = 83-10-192)
-- - Foreground '#262626' (Lch(uv) = 15-0-0)
-- - Accent chroma 80
if vim.o.background == 'light' then
palette = {
base00 = '#c0d2d2',
base01 = '#9badad',
base02 = '#778989',
base03 = '#546767',
base04 = '#353535',
base05 = '#262626',
base06 = '#181818',
base07 = '#040404',
base08 = '#402100',
base09 = '#855f00',
base0A = '#007d3c',
base0B = '#003d00',
base0C = '#b12985',
base0D = '#003fb6',
base0E = '#7e0052',
base0F = '#006cb4',
}
use_cterm = {
base00 = 252,
base01 = 248,
base02 = 102,
base03 = 241,
base04 = 237,
base05 = 235,
base06 = 234,
base07 = 232,
base08 = 235,
base09 = 94,
base0A = 29,
base0B = 22,
base0C = 126,
base0D = 25,
base0E = 89,
base0F = 25,
}
end
if palette then
require('mini.base16').setup({ palette = palette, use_cterm = use_cterm })
vim.g.colors_name = 'minicyan'
end

View File

@ -0,0 +1,95 @@
-- 'Minischeme' color scheme
-- Derived from base16 (https://github.com/chriskempson/base16) and
-- mini_palette palette generator
local use_cterm, palette
-- Dark palette is an output of 'MiniBase16.mini_palette':
-- - Background '#112641' (LCh(uv) = 15-20-250)
-- - Foreground '#e2e98f' (Lch(uv) = 90-60-90)
-- - Accent chroma 75
if vim.o.background == 'dark' then
palette = {
base00 = '#112641',
base01 = '#3a475e',
base02 = '#606b81',
base03 = '#8691a7',
base04 = '#d5dc81',
base05 = '#e2e98f',
base06 = '#eff69c',
base07 = '#fcffaa',
base08 = '#ffcfa0',
base09 = '#cc7e46',
base0A = '#46a436',
base0B = '#9ff895',
base0C = '#ca6ecf',
base0D = '#42f7ff',
base0E = '#ffc4ff',
base0F = '#00a5c5',
}
use_cterm = {
base00 = 235,
base01 = 238,
base02 = 242,
base03 = 246,
base04 = 186,
base05 = 186,
base06 = 229,
base07 = 229,
base08 = 223,
base09 = 173,
base0A = 71,
base0B = 156,
base0C = 170,
base0D = 51,
base0E = 189,
base0F = 38,
}
end
-- Light palette is an 'inverted dark', output of 'MiniBase16.mini_palette':
-- - Background '#e2e5ca' (LCh(uv) = 90-20-90)
-- - Foreground '#002a83' (Lch(uv) = 15-60-250)
-- - Accent chroma 75
if vim.o.background == 'light' then
palette = {
base00 = '#e2e5ca',
base01 = '#bcbfa4',
base02 = '#979a7e',
base03 = '#73765a',
base04 = '#324490',
base05 = '#002a83',
base06 = '#0000e4',
base07 = '#080500',
base08 = '#5e2200',
base09 = '#a86400',
base0A = '#008818',
base0B = '#004500',
base0C = '#b34aad',
base0D = '#004b76',
base0E = '#7d0077',
base0F = '#0086ae',
}
use_cterm = {
base00 = 254,
base01 = 250,
base02 = 246,
base03 = 243,
base04 = 239,
base05 = 18,
base06 = 20,
base07 = 232,
base08 = 52,
base09 = 130,
base0A = 28,
base0B = 22,
base0C = 133,
base0D = 24,
base0E = 90,
base0F = 31,
}
end
if palette then
require('mini.base16').setup({ palette = palette, use_cterm = use_cterm })
vim.g.colors_name = 'minischeme'
end

View File

@ -0,0 +1,16 @@
local hues = require('mini.hues')
-- Generate random config with initialized random seed (otherwise it won't be
-- random during startup)
math.randomseed(vim.loop.hrtime())
local base_colors = hues.gen_random_base_colors()
hues.setup({
background = base_colors.background,
foreground = base_colors.foreground,
n_hues = 8,
saturation = vim.o.background == 'dark' and 'medium' or 'high',
accent = 'bg',
})
vim.g.colors_name = 'randomhue'

View File

@ -0,0 +1,796 @@
*mini.ai* Extend and create a/i textobjects
*MiniAi*
MIT License Copyright (c) 2022 Evgeni Chasnovski
==============================================================================
Enhance some builtin |text-objects| (like |a(|, |a)|, |a'|, and more),
create new ones (like `a*`, `a<Space>`, `af`, `a?`, and more), and allow
user to create their own.
Features:
- Customizable creation of `a`/`i` textobjects using Lua patterns and functions.
Supports:
- Dot-repeat.
- |v:count|.
- Different search methods (see |MiniAi.config|).
- Consecutive application (update selection without leaving Visual mode).
- Aliases for multiple textobjects.
- Comprehensive builtin textobjects (see more in |MiniAi-textobject-builtin|):
- Balanced brackets (with and without whitespace) plus alias.
- Balanced quotes plus alias.
- Function call.
- Argument.
- Tag.
- Derived from user prompt.
- Default for punctuation, digit, space, or tab.
For more textobjects see |MiniExtra.gen_ai_spec|.
- Motions for jumping to left/right edge of textobject.
- Set of specification generators to tweak some builtin textobjects (see
|MiniAi.gen_spec|).
- Treesitter textobjects (through |MiniAi.gen_spec.treesitter()| helper).
This module works by defining mappings for both `a` and `i` in Visual and
Operator-pending mode. After typing, they wait for single character user input
treated as textobject identifier and apply resolved textobject specification
(fall back to other mappings if can't find proper textobject id). For more
information see |MiniAi-textobject-specification| and |MiniAi-algorithm|.
Known issues which won't be resolved:
- Search for builtin textobjects is done mostly using Lua patterns
(regex-like approach). Certain amount of false positives is to be expected.
- During search for builtin textobjects there is no distinction if it is
inside string or comment. For example, in the following case there will
be wrong match for a function call: `f(a = ")", b = 1)`.
General rule of thumb: any instrument using available parser for document
structure (like treesitter) will usually provide more precise results. This
module has builtins mostly for plain text textobjects which are useful
most of the times (like "inside brackets", "around quotes/underscore", etc.).
For advanced use cases define function specification for custom textobjects.
What it doesn't (and probably won't) do:
- Have special operators to specially handle whitespace (like `I` and `A`
in 'targets.vim'). Whitespace handling is assumed to be done inside
textobject specification (like `i(` and `i)` handle whitespace differently).
# Setup ~
This module needs a setup with `require('mini.ai').setup({})` (replace
`{}` with your `config` table). It will create global Lua table `MiniAi`
which you can use for scripting or manually (with `:lua MiniAi.*`).
See |MiniAi.config| for available config settings.
You can override runtime config settings (like `config.custom_textobjects`)
locally to buffer inside `vim.b.miniai_config` which should have same structure
as `MiniAi.config`. See |mini.nvim-buffer-local-config| for more details.
To stop module from showing non-error feedback, set `config.silent = true`.
# Comparisons ~
- 'wellle/targets.vim':
- Has limited support for creating own textobjects: it is constrained
to pre-defined detection rules. 'mini.ai' allows creating own rules
via Lua patterns and functions (see |MiniAi-textobject-specification|).
- Doesn't provide any programmatical API for getting information about
textobjects. 'mini.ai' does it via |MiniAi.find_textobject()|.
- Has no implementation of "moving to edge of textobject". 'mini.ai'
does it via |MiniAi.move_cursor()| and `g[` and `g]` default mappings.
- Has elaborate ways to control searching of the next textobject.
'mini.ai' relies on handful of 'config.search_method'.
- Implements `A`, `I` operators. 'mini.ai' does not by design: it is
assumed to be a property of textobject, not operator.
- Doesn't implement "function call" and "user prompt" textobjects.
'mini.ai' does (with `f` and `?` identifiers).
- Has limited support for "argument" textobject. Although it works in
most situations, it often misdetects commas as argument separator
(like if it is inside quotes or `{}`). 'mini.ai' deals with these cases.
- 'nvim-treesitter/nvim-treesitter-textobjects':
- Along with textobject functionality provides a curated and maintained
set of popular textobject queries for many languages (which can power
|MiniAi.gen_spec.treesitter()| functionality).
- Operates with custom treesitter directives (see
|lua-treesitter-directives|) allowing more fine-tuned textobjects.
- Implements only textobjects based on treesitter.
- Doesn't support |v:count|.
- Doesn't support multiple search method (basically, only 'cover').
- Doesn't support consecutive application of target textobject.
# Disabling ~
To disable, set `vim.g.miniai_disable` (globally) or `vim.b.miniai_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.
------------------------------------------------------------------------------
*MiniAi-textobject-builtin*
Builtin textobjects ~
This table describes all builtin textobjects along with what they
represent. Explanation:
- `Key` represents the textobject identifier: single alphanumeric,
punctuation, space, or tab character which should be typed after `a`/`i`.
- `Name` is a description of textobject.
- `Example line` contains a string for which examples are constructed. The
`*` denotes the cursor position.
- `a`/`i` describe inclusive region representing `a` and `i` textobjects.
Use numbers in separators for easier navigation.
- `2a`/`2i` describe either `2a`/`2i` (support for |v:count|) textobjects
or `a`/`i` textobject followed by another `a`/`i` textobject (consecutive
application leads to incremental selection).
Example: typing `va)` with cursor on `*` leads to selection from column 2
to column 12. Another typing `a)` changes selection to [1; 13]. Also, besides
visual selection, any |operator| can be used or `g[`/`g]` motions to move
to left/right edge of `a` textobject.
>
|Key| Name | Example line | a | i | 2a | 2i |
|---|---------------|-1234567890123456-|--------|--------|--------|--------|
| ( | Balanced () | (( *a (bb) )) | | | | |
| [ | Balanced [] | [[ *a [bb] ]] | [2;12] | [4;10] | [1;13] | [2;12] |
| { | Balanced {} | {{ *a {bb} }} | | | | |
| < | Balanced <> | << *a <bb> >> | | | | |
|---|---------------|-1234567890123456-|--------|--------|--------|--------|
| ) | Balanced () | (( *a (bb) )) | | | | |
| ] | Balanced [] | [[ *a [bb] ]] | | | | |
| } | Balanced {} | {{ *a {bb} }} | [2;12] | [3;11] | [1;13] | [2;12] |
| > | Balanced <> | << *a <bb> >> | | | | |
| b | Alias for | [( *a {bb} )] | | | | |
| | ), ], or } | | | | | |
|---|---------------|-1234567890123456-|--------|--------|--------|--------|
| " | Balanced " | "*a" " bb " | | | | |
| ' | Balanced ' | '*a' ' bb ' | | | | |
| ` | Balanced ` | `*a` ` bb ` | [1;4] | [2;3] | [6;11] | [7;10] |
| q | Alias for | '*a' " bb " | | | | |
| | ", ', or ` | | | | | |
|---|---------------|-1234567890123456-|--------|--------|--------|--------|
| ? | User prompt | e*e o e o o | [3;5] | [4;4] | [7;9] | [8;8] |
| |(typed e and o)| | | | | |
|---|---------------|-1234567890123456-|--------|--------|--------|--------|
| t | Tag | <x><y>*a</y></x> | [4;12] | [7;8] | [1;16] | [4;12] |
|---|---------------|-1234567890123456-|--------|--------|--------|--------|
| f | Function call | f(a, g(*b, c) ) | [6;13] | [8;12] | [1;15] | [3;14] |
|---|---------------|-1234567890123456-|--------|--------|--------|--------|
| a | Argument | f(*a, g(b, c) ) | [3;5] | [3;4] | [5;14] | [7;13] |
|---|---------------|-1234567890123456-|--------|--------|--------|--------|
| | Default | | | | | |
| | (digits, | aa_*b__cc___ | [4;7] | [4;5] | [8;12] | [8;9] |
| | punctuation, | (example for _) | | | | |
| | or whitespace)| | | | | |
|---|---------------|-1234567890123456-|--------|--------|--------|--------|
<
Notes:
- All examples assume default `config.search_method`.
- Open brackets differ from close brackets by how they treat inner edge
whitespace for `i` textobject: open ignores it, close - includes.
- Default textobject is activated for identifiers from digits (0, ..., 9),
punctuation (like `_`, `*`, `,`, etc.), whitespace (space, tab, etc.).
They are designed to be treated as separators, so include only right edge
in `a` textobject. To include both edges, use custom textobjects
(see |MiniAi-textobject-specification| and |MiniAi.config|). Note:
- When cursor is exactly on the identifier character while there are
two matching candidates on both left and right, the resulting region
with smaller width is preferred.
------------------------------------------------------------------------------
*MiniAi-glossary*
- REGION - table representing region in a buffer. Fields:
- <from> and <to> for inclusive start and end positions (<to> might be
`nil` to describe empty region). Each position is also a table with
line <line> and column <col> (both start at 1).
- <vis_mode> for which Visual mode will be used to select textobject.
See `opts` argument of |MiniAi.select_textobject()|.
One of `'v'`, `'V'`, `'\22'` (escaped `'<C-v>'`).
Examples: >lua
{ from = { line = 1, col = 1 }, to = { line = 2, col = 1 } }
-- Forced linewise mode
{
from = { line = 1, col = 1 }, to = { line = 2, col = 1 },
vis_mode = 'V',
}
-- Empty region
{ from = { line = 10, col = 10 } }
<
- PATTERN - string describing Lua pattern.
- SPAN - interval inside a string (end-exclusive). Like [1, 5). Equal
`from` and `to` edges describe empty span at that point.
- SPAN `A = [a1, a2)` COVERS `B = [b1, b2)` if every element of
`B` is within `A` (`a1 <= b < a2`).
It also is described as B IS NESTED INSIDE A.
- NESTED PATTERN - array of patterns aimed to describe nested spans.
- SPAN MATCHES NESTED PATTERN if there is a sequence of consecutively
nested spans each matching corresponding pattern within substring of
previous span (or input string for first span). Example: >lua
-- Nested patterns for balanced `()` with inner space
{ '%b()', '^. .* .$' }
-- Example input string (with columns underneath for easier reading):
"( ( () ( ) ) )"
-- 12345678901234
<
Here are all matching spans [1, 15) and [3, 13). Both [5, 7) and [8, 10)
match first pattern but not second. All other combinations of `(` and `)`
don't match first pattern (not balanced).
- COMPOSED PATTERN: array with each element describing possible pattern
(or array of them) at that place. Composed pattern basically defines all
possible combinations of nested pattern (their cartesian product).
Examples:
1. Either balanced `()` or balanced `[]` but both with inner edge space: >lua
-- Composed pattern
{ { '%b()', '%b[]' }, '^. .* .$' }
-- Composed pattern expanded into equivalent array of nested patterns
{ '%b()', '^. .* .$' } -- and
{ '%b[]', '^. .* .$' }
<
2. Either "balanced `()` with inner edge space" or "balanced `[]` with
no inner edge space", both with 5 or more characters: >lua
-- Composed pattern
{ { { '%b()', '^. .* .$' }, { '%b[]', '^.[^ ].*[^ ].$' } }, '.....' }
-- Composed pattern expanded into equivalent array of nested patterns
{ '%b()', '^. .* .$', '.....' } -- and
{ '%b[]', '^.[^ ].*[^ ].$', '.....' }
<
- SPAN MATCHES COMPOSED PATTERN if it matches at least one nested pattern
from expanded composed pattern.
------------------------------------------------------------------------------
*MiniAi-textobject-specification*
Textobject specification has a structure of composed pattern (see
|MiniAi-glossary|) with two differences:
- Last pattern(s) should have even number of empty capture groups denoting
how the last string should be processed to extract `a` or `i` textobject:
- Zero captures mean that whole string represents both `a` and `i`.
Example: `xxx` will define textobject matching string `xxx` literally.
- Two captures represent `i` textobject inside of them. `a` - whole string.
Example: `x()x()x` defines `a` textobject to be `xxx`, `i` - middle `x`.
- Four captures define `a` textobject inside captures 1 and 4, `i` -
inside captures 2 and 3. Example: `x()()x()x()` defines `a`
textobject to be last `xx`, `i` - middle `x`.
- Allows callable objects (see |vim.is_callable()|) in certain places
(enables more complex textobjects in exchange of increase in configuration
complexity and computations):
- If specification itself is a callable, it will be called with the same
arguments as |MiniAi.find_textobject()| and should return one of:
- Composed pattern. Useful for implementing user input. Example of
simplified variant of textobject for function call with name taken
from user prompt: >lua
function()
local left_edge = vim.pesc(vim.fn.input('Function name: '))
return { left_edge .. '%b()', '^.-%(().*()%)$' }
end
<
- Single output region. Useful to allow full control over
textobject. Will be taken as is. Example of returning whole buffer: >lua
function()
local from = { line = 1, col = 1 }
local to = {
line = vim.fn.line('$'),
col = math.max(vim.fn.getline('$'):len(), 1)
}
return { from = from, to = to, vis_mode = 'V' }
end
<
- Array of output region(s). Useful for incorporating other
instruments, like treesitter (see |MiniAi.gen_spec.treesitter()|).
The best region will be picked in the same manner as with composed
pattern (respecting options `n_lines`, `search_method`, etc.).
Example of selecting "best" line with display width more than 80: >lua
function(_, _, _)
local res = {}
for i = 1, vim.api.nvim_buf_line_count(0) do
local cur_line = vim.fn.getline(i)
if vim.fn.strdisplaywidth(cur_line) > 80 then
local region = {
from = { line = i, col = 1 },
to = { line = i, col = cur_line:len() },
}
table.insert(res, region)
end
end
return res
end
<
- If there is a callable instead of assumed string pattern, it is expected
to have signature `(line, init)` and behave like `pattern:find()`.
It should return two numbers representing span in `line` next after
or at `init` (`nil` if there is no such span).
!IMPORTANT NOTE!: it means that output's `from` shouldn't be strictly
to the left of `init` (it will lead to infinite loop). Not allowed as
last item (as it should be pattern with captures).
Example of matching only balanced parenthesis with big enough width: >lua
{
'%b()',
function(s, init)
if init > 1 or s:len() < 5 then return end
return 1, s:len()
end,
'^.().*().$'
}
<
More examples: >lua
-- Pair of balanced brackets from set (used for builtin `b` identifier):
{ { '%b()', '%b[]', '%b{}' }, '^.().*().$' }
-- Imitate word ignoring digits and punctuation (only for Latin alphabet):
{ '()()%f[%w]%w+()[ \t]*()' }
-- Word with camel case support (also supports only Latin alphabet):
{
{
'%u[%l%d]+%f[^%l%d]',
'%f[%S][%l%d]+%f[^%l%d]',
'%f[%P][%l%d]+%f[^%l%d]',
'^[%l%d]+%f[^%l%d]',
},
'^().*()$'
}
-- Number:
{ '%f[%d]%d+' }
-- Date in 'YYYY-MM-DD' format:
{ '()%d%d%d%d%-%d%d%-%d%d()' }
-- Lua block string:
{ '%[%[().-()%]%]' }
<
See |MiniAi.gen_spec| for function wrappers to create commonly used
textobject specifications.
------------------------------------------------------------------------------
*MiniAi-algorithm*
Algorithm design
Search for the textobjects relies on these principles:
- It uses same input data as described in |MiniAi.find_textobject()|,
i.e. whether it is `a` or `i` textobject, its identifier, reference region, etc.
- Textobject specification is constructed based on textobject identifier
(see |MiniAi-textobject-specification|).
- General search is done by converting some 2d buffer region (neighborhood
of reference region) into 1d string (each line is appended with `\n`).
Then search for a best span matching textobject specification is done
inside string (see |MiniAi-glossary|). After that, span is converted back
into 2d region. Note: first search is done inside reference region lines,
and only after that - inside its neighborhood within `config.n_lines`
(see |MiniAi.config|).
- The best matching span is chosen by iterating over all spans matching
textobject specification and comparing them with "current best".
Comparison also depends on reference region (tighter covering is better,
otherwise closer is better) and search method (if span is even considered).
- Extract span based on extraction pattern (last item in nested pattern).
- If task is to perform a consecutive search (`opts.n_times` is greater than 1),
steps are repeated with current best match becoming reference region.
One such additional step is also done if final region is equal to
reference region (this enables consecutive application).
Notes:
- Iteration over all matched spans is done in depth-first fashion with
respect to nested pattern.
- It is guaranteed that span is compared only once.
- For the sake of increasing functionality, during iteration over all
matching spans, some Lua patterns in composed pattern are handled
specially.
- `%bxx` (`xx` is two identical characters). It denotes balanced pair
of identical characters and results into "paired" matches. For
example, `%b""` for `"aa" "bb"` would match `"aa"` and `"bb"`, but
not middle `" "`.
- `x.-y` (`x` and `y` are different strings). It results only in matches with
smallest width. For example, `e.-o` for `e e o o` will result only in
middle `e o`. Note: it has some implications for when parts have
quantifiers (like `+`, etc.), which usually can be resolved with
frontier pattern `%f[]` (see examples in |MiniAi-textobject-specification|).
------------------------------------------------------------------------------
*MiniAi.setup()*
`MiniAi.setup`({config})
Module setup
Parameters ~
{config} `(table|nil)` Module config table. See |MiniAi.config|.
Usage ~
>lua
require('mini.ai').setup() -- use default config
-- OR
require('mini.ai').setup({}) -- replace {} with your config table
<
------------------------------------------------------------------------------
*MiniAi.config*
`MiniAi.config`
Module config
Default values:
>lua
MiniAi.config = {
-- Table with textobject id as fields, textobject specification as values.
-- Also use this to disable builtin textobjects. See |MiniAi.config|.
custom_textobjects = nil,
-- Module mappings. Use `''` (empty string) to disable one.
mappings = {
-- Main textobject prefixes
around = 'a',
inside = 'i',
-- Next/last textobjects
around_next = 'an',
inside_next = 'in',
around_last = 'al',
inside_last = 'il',
-- Move cursor to corresponding edge of `a` textobject
goto_left = 'g[',
goto_right = 'g]',
},
-- Number of lines within which textobject is searched
n_lines = 50,
-- How to search for object (first inside current line, then inside
-- neighborhood). One of 'cover', 'cover_or_next', 'cover_or_prev',
-- 'cover_or_nearest', 'next', 'prev', 'nearest'.
search_method = 'cover_or_next',
-- Whether to disable showing non-error feedback
silent = false,
}
<
# Options ~
## Custom textobjects ~
Each named entry of `config.custom_textobjects` is a textobject with
that identifier and specification (see |MiniAi-textobject-specification|).
They are also used to override builtin ones (|MiniAi-textobject-builtin|).
Supply non-valid input (not in specification format) to disable module's
builtin textobject in favor of external or Neovim's builtin mapping.
Examples:
>lua
require('mini.ai').setup({
custom_textobjects = {
-- Tweak argument textobject
a = require('mini.ai').gen_spec.argument({ brackets = { '%b()' } }),
-- Disable brackets alias in favor of builtin block textobject
b = false,
-- Now `vax` should select `xxx` and `vix` - middle `x`
x = { 'x()x()x' },
-- Whole buffer
g = function()
local from = { line = 1, col = 1 }
local to = {
line = vim.fn.line('$'),
col = math.max(vim.fn.getline('$'):len(), 1)
}
return { from = from, to = to }
end
}
})
-- Use `vim.b.miniai_config` to customize per buffer
-- Example of specification useful for Markdown files:
local spec_pair = require('mini.ai').gen_spec.pair
vim.b.miniai_config = {
custom_textobjects = {
['*'] = spec_pair('*', '*', { type = 'greedy' }),
['_'] = spec_pair('_', '_', { type = 'greedy' }),
},
}
<
There are more example specifications in |MiniAi-textobject-specification|.
## Search method ~
Value of `config.search_method` defines how best match search is done.
Based on its value, one of the following matches will be selected:
- Covering match. Left/right edge is before/after left/right edge of
reference region.
- Previous match. Left/right edge is before left/right edge of reference
region.
- Next match. Left/right edge is after left/right edge of reference region.
- Nearest match. Whichever is closest among previous and next matches.
Possible values are:
- `'cover'` - use only covering match. Don't use either previous or
next; report that there is no textobject found.
- `'cover_or_next'` (default) - use covering match. If not found, use next.
- `'cover_or_prev'` - use covering match. If not found, use previous.
- `'cover_or_nearest'` - use covering match. If not found, use nearest.
- `'next'` - use next match.
- `'prev'` - use previous match.
- `'nearest'` - use nearest match.
Note: search is first performed on the reference region lines and only
after failure - on the whole neighborhood defined by `config.n_lines`. This
means that with `config.search_method` not equal to `'cover'`, "prev" or
"next" textobject will end up as search result if they are found on first
stage although covering match might be found in bigger, whole neighborhood.
This design is based on observation that most of the time operation is done
within reference region lines (usually cursor line).
Here is an example of what `a)` textobject is based on a value of
`'config.search_method'` when cursor is inside `bbb` word:
- `'cover'`: `(a) bbb (c)` -> none
- `'cover_or_next'`: `(a) bbb (c)` -> `(c)`
- `'cover_or_prev'`: `(a) bbb (c)` -> `(a)`
- `'cover_or_nearest'`: depends on cursor position.
For first and second `b` - as in `cover_or_prev` (as previous match is
nearer), for third - as in `cover_or_next` (as next match is nearer).
- `'next'`: `(a) bbb (c)` -> `(c)`. Same outcome for `(bbb)`.
- `'prev'`: `(a) bbb (c)` -> `(a)`. Same outcome for `(bbb)`.
- `'nearest'`: depends on cursor position (same as in `'cover_or_nearest'`).
## Mappings ~
Mappings `around_next`/`inside_next` and `around_last`/`inside_last` are
essentially `around`/`inside` but using search method `'next'` and `'prev'`.
------------------------------------------------------------------------------
*MiniAi.find_textobject()*
`MiniAi.find_textobject`({ai_type}, {id}, {opts})
Find textobject region
Parameters ~
{ai_type} `(string)` One of `'a'` or `'i'`.
{id} `(string)` Single character string representing textobject id. It is
used to get specification which is later used to compute textobject region.
Note: if specification is a function, it is called with all present
arguments (`opts` is populated with default arguments).
{opts} `(table|nil)` Options. Possible fields:
- <n_lines> - Number of lines within which textobject is searched.
Default: `config.n_lines` (see |MiniAi.config|).
- <n_times> - Number of times to perform a consecutive search. Each one
is done with reference region being previous found textobject region.
Default: 1.
- <reference_region> - region to try to cover (see |MiniAi-glossary|). It
is guaranteed that output region will not be inside or equal to this one.
Default: empty region at cursor position.
- <search_method> - Search method. Default: `config.search_method`.
Return ~
`(table|nil)` Region of textobject or `nil` if no textobject different
from `opts.reference_region` was consecutively found `opts.n_times` times.
------------------------------------------------------------------------------
*MiniAi.move_cursor()*
`MiniAi.move_cursor`({side}, {ai_type}, {id}, {opts})
Move cursor to edge of textobject
Parameters ~
{side} `(string)` One of `'left'` or `'right'`.
{ai_type} `(string)` One of `'a'` or `'i'`.
{id} `(string)` Single character string representing textobject id.
{opts} `(table|nil)` Same as in |MiniAi.find_textobject()|.
`opts.n_times` means number of actual jumps (important when cursor
already on the potential jump spot).
------------------------------------------------------------------------------
*MiniAi.gen_spec*
`MiniAi.gen_spec`
Generate common textobject specifications
This is a table with function elements. Call to actually get specification.
Example: >lua
local gen_spec = require('mini.ai').gen_spec
require('mini.ai').setup({
custom_textobjects = {
-- Tweak argument to be recognized only inside `()` between `;`
a = gen_spec.argument({ brackets = { '%b()' }, separator = ';' }),
-- Tweak function call to not detect dot in function name
f = gen_spec.function_call({ name_pattern = '[%w_]' }),
-- Function definition (needs treesitter queries with these captures)
F = gen_spec.treesitter({ a = '@function.outer', i = '@function.inner' }),
-- Make `|` select both edges in non-balanced way
['|'] = gen_spec.pair('|', '|', { type = 'non-balanced' }),
}
})
------------------------------------------------------------------------------
*MiniAi.gen_spec.argument()*
`MiniAi.gen_spec.argument`({opts})
Argument specification
Argument textobject (has default `a` identifier) is a region inside
balanced bracket between allowed not excluded separators. Use this function
to tweak how it works.
Examples:
- `argument({ brackets = { '%b()' } })` will search for an argument only
inside balanced `()`.
- `argument({ separator = '[,;]' })` will treat both `,` and `;` as separators.
- `argument({ exclude_regions = { '%b()' } })` will exclude separators
which are inside balanced `()` (inside outer brackets).
Parameters ~
{opts} `(table|nil)` Options. Allowed fields:
- <brackets> - array of patterns for outer balanced brackets.
Default: `{ '%b()', '%b[]', '%b{}' }` (any `()`, `[]`, or `{}` can
enclose arguments).
- <separator> - separator pattern. Default: `','`.
One of the practical usages of this option is to include whitespace
around character to be a part of separator. For example, `'%s*,%s*'`
will treat as separator not only ',', but its possible surrounding
whitespace. This has both positive and negative effects. On one hand,
`daa` executed over the first argument will delete whitespace after
first comma, leading to a more expected outcome. On the other hand it
is ambiguous which argument is picked when cursor is over whitespace
near the character separator.
- <exclude_regions> - array with patterns for regions inside which
separators will be ignored.
Default: `{ '%b""', "%b''", '%b()', '%b[]', '%b{}' }` (separators
inside balanced quotes or brackets are ignored).
------------------------------------------------------------------------------
*MiniAi.gen_spec.function_call()*
`MiniAi.gen_spec.function_call`({opts})
Function call specification
Function call textobject (has default `f` identifier) is a region with some
characters followed by balanced `()`. Use this function to tweak how it works.
Example:
- `function_call({ name_pattern = '[%w_]' })` will recognize function name with
only alphanumeric or underscore (not dot).
Parameters ~
{opts} `(table|nil)` Optsion. Allowed fields:
- <name_pattern> - string pattern of character set allowed in function name.
Default: `'[%w_%.]'` (alphanumeric, underscore, or dot).
Note: should be enclosed in `[]`.
------------------------------------------------------------------------------
*MiniAi.gen_spec.pair()*
`MiniAi.gen_spec.pair`({left}, {right}, {opts})
Pair specification
Use it to define textobject for region surrounded with `left` from left and
`right` from right. The `a` textobject includes both edges, `i` - excludes them.
Region can be one of several types (controlled with `opts.type`). All
examples are for default search method, `a` textobject, and use `'_'` as
both `left` and `right`:
- Non-balanced (`{ type = 'non-balanced' }`), default. Equivalent to using
`x.-y` as first pattern. Example: on line '_a_b_c_' it consecutively
matches '_a_', '_b_', '_c_'.
- Balanced (`{ type = 'balanced' }`). Equivalent to using `%bxy` as first
pattern. Example: on line '_a_b_c_' it consecutively matches '_a_', '_c_'.
Note: both `left` and `right` should be single character.
- Greedy (`{ type = 'greedy' }`). Like non-balanced but will select maximum
consecutive `left` and `right` edges. Example: on line '__a__b_' it
consecutively selects '__a__' and '__b_'. Note: both `left` and `right`
should be single character.
Parameters ~
{left} `(string)` Left edge.
{right} `(string)` Right edge.
{opts} `(table|nil)` Options. Possible fields:
- <type> - Type of a pair. One of `'non-balanced'` (default), `'balanced'`,
`'greedy'`.
------------------------------------------------------------------------------
*MiniAi.gen_spec.treesitter()*
`MiniAi.gen_spec.treesitter`({ai_captures}, {opts})
Treesitter specification
This is a specification in function form. When called with a pair of
treesitter captures, it returns a specification function outputting an
array of regions that match corresponding (`a` or `i`) capture.
In order for this to work, apart from working treesitter parser for desired
language, user should have a reachable language-specific 'textobjects'
query (see |vim.treesitter.query.get()| or |get_query()|, depending on your
Neovim version).
The most straightforward way for this is to have 'textobjects.scm' query
file with treesitter captures stored in some recognized path. This is
primarily designed to be compatible with plugin
'nvim-treesitter/nvim-treesitter-textobjects', but can be used without it.
Two most common approaches for having a query file:
- Install 'nvim-treesitter/nvim-treesitter-textobjects'. It has curated and
well maintained builtin query files for many languages with a standardized
capture names, like `function.outer`, `function.inner`, etc.
- Manually create file 'after/queries/<language name>/textobjects.scm' in
your |$XDG_CONFIG_HOME| directory. It should contain queries with
captures (later used to define textobjects). See |lua-treesitter-query|.
To verify that query file is reachable, run (example for "lua" language,
output should have at least an intended file): >vim
:lua print(vim.inspect(vim.treesitter.query.get_files('lua','textobjects')))
<
Example configuration for function definition textobject with
'nvim-treesitter/nvim-treesitter-textobjects' captures:
>lua
local spec_treesitter = require('mini.ai').gen_spec.treesitter
require('mini.ai').setup({
custom_textobjects = {
F = spec_treesitter({ a = '@function.outer', i = '@function.inner' }),
o = spec_treesitter({
a = { '@conditional.outer', '@loop.outer' },
i = { '@conditional.inner', '@loop.inner' },
})
}
})
<
Notes:
- By default query is done using 'nvim-treesitter' plugin if it is present
(falls back to builtin methods otherwise). This allows for a more
advanced features (like multiple buffer languages, custom directives, etc.).
See `opts.use_nvim_treesitter` for how to disable this.
- It uses buffer's |filetype| to determine query language.
- On large files it is slower than pattern-based textobjects. Still very
fast though (one search should be magnitude of milliseconds or tens of
milliseconds on really large file).
Parameters ~
{ai_captures} `(table)` Captures for `a` and `i` textobjects: table with
<a> and <i> fields with captures for `a` and `i` textobjects respectively.
Each value can be either a string capture (should start with `'@'`) or an
array of such captures (best among all matches will be chosen).
{opts} `(table|nil)` Options. Possible values:
- <use_nvim_treesitter> - whether to try to use 'nvim-treesitter' plugin
(if present) to do the query. It implements more advanced behavior at
cost of increased execution time. Provides more coherent experience if
'nvim-treesitter-textobjects' queries are used. Default: `true`.
Return ~
`(function)` Function with |MiniAi.find_textobject()| signature which
returns array of current buffer regions representing matches for
corresponding (`a` or `i`) treesitter capture.
See also ~
|MiniAi-textobject-specification| for how this type of textobject
specification is processed.
|get_query()| for how query is fetched in case of no 'nvim-treesitter'.
|Query:iter_captures()| for how all query captures are iterated in case of
no 'nvim-treesitter'.
------------------------------------------------------------------------------
*MiniAi.select_textobject()*
`MiniAi.select_textobject`({ai_type}, {id}, {opts})
Visually select textobject region
Does nothing if no region is found.
Parameters ~
{ai_type} `(string)` One of `'a'` or `'i'`.
{id} `(string)` Single character string representing textobject id.
{opts} `(table|nil)` Same as in |MiniAi.find_textobject()|. Extra fields:
- <vis_mode> - One of `'v'`, `'V'`, or `'\22'` (escaped version of `'<C-v>'`).
Default: Latest visual mode.
- <operator_pending> - Whether selection is for Operator-pending mode.
Used in that mode's mappings, shouldn't be used directly. Default: `false`.
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -0,0 +1,907 @@
*mini.align* Align text interactively
*MiniAlign*
MIT License Copyright (c) 2022 Evgeni Chasnovski
==============================================================================
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.
Features:
- Alignment is done in three main steps:
- <Split> lines into parts based on Lua pattern(s) or user-supplied rule.
- <Justify> parts for certain side(s) to be same width inside columns.
- <Merge> parts to be lines, with customizable delimiter(s).
Each main step can be preceded by other steps (pre-steps) to achieve
highly customizable outcome. See `steps` value in |MiniAlign.config|. For
more details, see |MiniAlign-glossary| and |MiniAlign-algorithm|.
- User can control alignment interactively by pressing customizable modifiers
(single keys representing how alignment steps and/or options should change).
Some of default modifiers:
- Press `s` to enter split Lua pattern.
- Press `j` to choose justification side from available ones ("left",
"center", "right", "none").
- Press `m` to enter merge delimiter.
- Press `f` to enter filter Lua expression to configure which parts
will be affected (like "align only first column").
- Press `i` to ignore some commonly unwanted split matches.
- Press `p` to pair neighboring parts so they be aligned together.
- Press `t` to trim whitespace from parts.
- Press `<BS>` (backspace) to delete some last pre-step.
For more details, see |MiniAlign-modifiers-builtin| and |MiniAlign-examples|.
- Alignment can be done with instant preview (result is updated after each
modifier) or without it (result is shown and accepted after non-default
split pattern is set).
- Every user interaction is accompanied with helper status message showing
relevant information about current alignment process.
# Setup ~
This module needs a setup with `require('mini.align').setup({})` (replace
`{}` with your `config` table). It will create global Lua table `MiniAlign`
which you can use for scripting or manually (with `:lua MiniAlign.*`).
See |MiniAlign.config| for available config settings.
You can override runtime config settings (like `config.modifiers`) locally
to buffer inside `vim.b.minialign_config` which should have same structure
as `MiniAlign.config`. See |mini.nvim-buffer-local-config| for more details.
To stop module from showing non-error feedback, set `config.silent = true`.
# Comparisons ~
- 'junegunn/vim-easy-align':
- 'mini.align' is mostly designed after 'junegunn/vim-easy-align', so
there are a lot of similarities.
- Both plugins allow users to change alignment options interactively by
pressing modifier keys (albeit completely different default ones).
'junegunn/vim-easy-align' has those modifiers fixed, while 'mini.align'
allows their full customization. See |MiniAlign.config| for examples.
- 'junegunn/vim-easy-align' is designed to treat delimiters differently
than other parts of strings. 'mini.align' doesn't distinguish split
parts from one another by design: splitting is allowed to be done
based on some other logic than by splitting on delimiters.
- 'junegunn/vim-easy-align' initially aligns by only first delimiter.
'mini.align' initially aligns by all delimiter.
- 'junegunn/vim-easy-align' implements special filtering by delimiter
row number. 'mini.align' has builtin filtering based on Lua code
supplied by user in modifier phase. See |MiniAlign.gen_step.filter|
and 'f' builtin modifier.
- 'mini.align' treats any non-registered modifier as a plain delimiter
pattern, while 'junegunn/vim-easy-align' does not.
- 'mini.align' exports core Lua function used for aligning strings
(|MiniAlign.align_strings()|).
- 'godlygeek/tabular':
- 'godlygeek/tabular' is mostly designed around single command which is
customized by printing its parameters. 'mini.align' implements
different concept of interactive alignment through pressing
customizable single character modifiers.
- 'godlygeek/tabular' can detect region upon which alignment can be
desirable. 'mini.align' does not by design: use Visual selection or
textobject/motion to explicitly define region to align.
# Disabling ~
To disable, set `vim.g.minialign_disable` (globally) or `vim.b.minialign_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.
------------------------------------------------------------------------------
*MiniAlign-glossary*
Glossary
PARTS 2d array of strings (array of arrays of strings).
See more in |MiniAlign.as_parts()|.
ROW First-level array of parts (like `parts[1]`).
COLUMN Array of strings, constructed from parts elements with the same
second-level index (like `{ parts[1][1],` `parts[2][1], ... }`).
STEP A named callable. See |MiniAlign.new_step()|. When used in terms of
alignment steps, callable takes two arguments: some object (parts
or string array) and option table.
SPLIT Process of taking array of strings and converting it into parts.
JUSTIFY Process of taking parts and converting them to aligned parts (all
elements have same widths inside columns).
MERGE Process of taking parts and converting it back to array of strings.
Usually by concatenating rows into strings.
REGION Table representing region in a buffer. Fields: <from> and <to> for
inclusive start and end positions (<to> might be `nil` to describe
empty region). Each position is also a table with line <line> and
column <col> (both start at 1).
MODE Either charwise ("char", `v`, |charwise|), linewise ("line", `V`,
|linewise|) or blockwise ("block", `<C-v>`, |blockwise-visual|)
------------------------------------------------------------------------------
*MiniAlign-algorithm*
Algorithm design
There are two main processes implemented in 'mini.align': strings alignment
and interactive region alignment. See |MiniAlign-glossary| for more information
about used terms.
Strings alignment ~
Main implementation is in |MiniAlign.align_strings()|. Its input is array of
strings and output - array of aligned strings. The process consists from three
main steps (split, justify, merge) which can be preceded by any number of
preliminary steps (pre-split, pre-justify, pre-merge).
Algorithm:
- <Pre-split>. Take input array of strings and consecutively apply all
pre-split steps (`steps.pre_split`). Each one has `(strings, opts)` signature
and should modify array in place.
- <Split>. Take array of strings and convert it to parts with `steps.split()`.
It has `(strings, opts)` signature and should return parts.
- <Pre-justify>. Take parts and consecutively apply all pre-justify
steps (`steps.pre_justify`). Each one has `(parts, opts)` signature and
should modify parts in place.
- <Justify>. Take parts and apply `steps.justify()`. It has `(parts, opts)`
signature and should modify parts in place.
- <Pre-merge>. Take parts and consecutively apply all pre-merge
steps (`steps.pre_merge`). Each one has `(parts, opts)` signature and
should modify parts in place.
- <Merge>. Take parts and convert it to array of strings with `steps.merge()`.
It has `(parts, opts)` signature and should return array of strings.
Notes:
- All table objects are initially copied so that modification in place doesn't
affect workflow.
- Default main steps are designed to be controlled via options. See
|MiniAlign.align_strings()| and default step entries in |MiniAlign.gen_step|.
- All steps are guaranteed to take same option table as second argument.
This allows steps to "talk" to each other, i.e. earlier steps can pass data
to later ones.
Interactive region alignment ~
Interactive alignment is a main entry point for most users. It can be done
in two flavors:
- <Without preview>. Initiated via mapping defined in `start` of
`MiniAlign.config.mappings`. Alignment is accepted once split pattern becomes
non-default.
- <With preview>. Initiated via mapping defined in `start_with_preview` of
`MiniAlign.config.mappings`. Alignment result is shown after every modifier
and is accepted after `<CR>` (`Enter`) is hit. Note: each preview is done by
applying current alignment steps and options to the initial region lines,
not the ones currently displaying in preview.
Lifecycle (assuming default mappings):
- <Initiate alignment>:
- In Normal mode type `ga` (or `gA` to show preview) followed by textobject
or motion defining region to be aligned.
- In Visual mode select region and type `ga` (or `gA` to show preview).
Strings contained in selected region will be used as input to
|MiniAlign.align_strings()|.
Beware of mode when selecting region: charwise (`v`), linewise (`V`), or
blockwise (`<C-v>`). They all behave differently.
- <Press modifiers>. Press single keys one at a time:
- If pressed key is among table keys of `modifiers` table of
|MiniAlign.config|, its function value is executed. It usually modifies
some options(s) and/or affects some pre-step(s).
- If pressed key is not among defined modifiers, it is treated as plain
split pattern.
This process can either end by itself (usually in case of no preview and
non-default split pattern being set) or you can choose to end it manually.
- <Accept or discard>. In case of active preview, accept current result by
pressing `<CR>`. Discard any result and return to initial regions with
either `<Esc>` or `<C-c>`.
See more in |MiniAlign-modifiers-builtin| and |MiniAlign-examples|.
Notes:
- Visual blockwise selection works best with 'virtualedit' equal to "block"
or "all".
------------------------------------------------------------------------------
*MiniAlign-modifiers-builtin*
Overview of builtin modifiers
All examples assume interactive alignment with preview in linewise mode. With
default mappings, use `V` to select lines and `gA` to initiate alignment. It
might be helpful to copy lines into modifiable buffer and experiment yourself.
Notes:
- Any pressed key which doesn't have defined modifier will be treated as
plain split pattern.
- All modifiers can be customized inside |MiniAlign.setup|. See "Modifiers"
section of |MiniAlign.config|.
Main option modifiers ~
<s> Enter split pattern (confirm prompt by pressing `<CR>`). Input is treated
as plain delimiter.
Before: >
a-b-c
aa-bb-cc
<
After typing `s-<CR>`: >
a -b -c
aa-bb-cc
<
<j> Choose justify side. Prompts user (with helper message) to type single
character identifier of side: `l`eft, `c`enter, `r`ight, `n`one.
Before: >
a_b_c
aa_bb_cc
<
After typing `_jr` (first make split by `_`): >
a_ b_ c
aa_bb_cc
<
<m> Enter merge delimiter (confirm prompt by pressing `<CR>`).
Before: >
a_b_c
aa_bb_cc
<
After typing `_m--<CR>` (first make split by `_`): >
a --_--b --_--c
aa--_--bb--_--cc
<
Modifiers adding pre-steps ~
<f> Enter filter expression. See more details in |MiniAlign.gen_step.filter()|.
Before: >
a_b_c
aa_bb_cc
<
After typing `_fn==1<CR>` (first make split by `_`): >
a _b_c
aa_bb_cc
<
<i> Ignore some split matches. It modifies `split_exclude_patterns` option by
adding commonly wanted patterns. See more details in
|MiniAlign.gen_step.ignore_split()|.
Before: >
/* This_is_assumed_to_be_comment */
a"_"_b
aa_bb
<
After typing `_i` (first make split by `_`): >
/* This_is_assumed_to_be_comment */
a"_"_b
aa _bb
<
<p> Pair neighboring parts.
Before: >
a_b_c
aaa_bbb_ccc
<
After typing `_p` (first make split by `_`): >
a_ b_ c
aaa_bbb_ccc
<
<t> Trim parts from whitespace on both sides (keeping indentation).
Before: >
a _ b _ c
aa _bb _cc
<
After typing `_t` (first make split by `_`): >
a _b _c
aa_bb_cc
<
Delete some last pre-step ~
<BS> Delete one of the pre-steps. If there is only one kind of pre-steps,
remove its latest added one. If not, prompt user to choose pre-step kind
by entering single character: `s`plit, `j`ustify, `m`erge.
Examples:
- `tp<BS>` results in only "trim" step to be left.
- `it<BS>` prompts to choose which step to delete (pre-split or
pre-justify in this case).
Special configurations for common splits ~
<=> Use special pattern to align by a group of consecutive "=". It can be
preceded by any number of punctuation marks and followed by some sommon
punctuation characters. Trim whitespace and merge with single space.
Before: >
a=b
aa<=bb
aaa===bbb
aaaa = cccc
<
After typing `=`: >
a = b
aa <= bb
aaa === bbb
aaaa = cccc
<
<,> Besides splitting by "," character, trim whitespace, pair neighboring
parts and merge with single space.
Before: >
a,b
aa,bb
aaa , bbb
<
After typing `,`: >
a, b
aa, bb
aaa, bbb
<
< > (Space bar) Squash consecutive whitespace into single single space (accept
possible indentation) and split by `%s+` pattern (keeps indentation).
Before: >
a b c
aa bb cc
<
After typing `<Space>`: >
a b c
aa bb cc
------------------------------------------------------------------------------
*MiniAlign-examples*
More complex examples to explore functionality
Copy lines in modifiable buffer, initiate alignment with preview (`gAip`)
and try typing suggested key sequences.
These are modified examples taken from 'junegunn/vim-easy-align'.
Equal sign ~
Lines: >
# This=is=assumed=to be a comment
"a ="
a =
a = 1
bbbb = 2
ccccccc = 3
ccccccccccccccc
ddd = 4
eeee === eee = eee = eee=f
fff = ggg += gg &&= gg
g != hhhhhhhh == 888
i := 5
i %= 5
i *= 5
j =~ 5
j >= 5
aa => 123
aa <<= 123
aa >>= 123
bbb => 123
c => 1233123
d => 123
dddddd &&= 123
dddddd ||= 123
dddddd /= 123
gg <=> ee
<
Key sequences:
- `=`
- `=jc`
- `=jr`
- `=m!<CR>`
- `=p`
- `=i` (execute `:lua vim.o.commentstring = '# %s'` for full experience)
- `=<BS>`
- `=<BS>p`
- `=fn==1<CR>`
- `=<BS>fn==1<CR>t`
- `=frow>7<CR>`
------------------------------------------------------------------------------
*MiniAlign.setup()*
`MiniAlign.setup`({config})
Module setup
Parameters ~
{config} `(table|nil)` Module config table. See |MiniAlign.config|.
Usage ~
>lua
require('mini.align').setup() -- use default config
-- OR
require('mini.align').setup({}) -- replace {} with your config table
<
------------------------------------------------------------------------------
*MiniAlign.config*
`MiniAlign.config`
Module config
Default values:
>lua
MiniAlign.config = {
-- Module mappings. Use `''` (empty string) to disable one.
mappings = {
start = 'ga',
start_with_preview = 'gA',
},
-- Modifiers changing alignment steps and/or options
modifiers = {
-- Main option modifiers
['s'] = --<function: enter split pattern>,
['j'] = --<function: choose justify side>,
['m'] = --<function: enter merge delimiter>,
-- Modifiers adding pre-steps
['f'] = --<function: filter parts by entering Lua expression>,
['i'] = --<function: ignore some split matches>,
['p'] = --<function: pair parts>,
['t'] = --<function: trim parts>,
-- Delete some last pre-step
['<BS>'] = --<function: delete some last pre-step>,
-- Special configurations for common splits
['='] = --<function: enhanced setup for '='>,
[','] = --<function: enhanced setup for ','>,
[' '] = --<function: enhanced setup for ' '>,
},
-- Default options controlling alignment process
options = {
split_pattern = '',
justify_side = 'left',
merge_delimiter = '',
},
-- Default steps performing alignment (if `nil`, default is used)
steps = {
pre_split = {},
split = nil,
pre_justify = {},
justify = nil,
pre_merge = {},
merge = nil,
},
-- Whether to disable showing non-error feedback
silent = false,
}
<
# Options ~
## Modifiers ~
`MiniAlign.config.modifiers` is used to define interactive user experience
of managing alignment process. It is a table with single character keys and
modifier function values.
Each modifier function:
- Is called when corresponding modifier key is pressed.
- Has signature `(steps, opts)` and should modify any of its input in place.
Examples:
- Modifier function used for default 'i' modifier: >lua
function(steps, _)
table.insert(steps.pre_split, MiniAlign.gen_step.ignore_split())
end
<
- Tweak 't' modifier to use highest indentation instead of keeping it: >lua
require('mini.align').setup({
modifiers = {
t = function(steps, _)
local trim_high = MiniAlign.gen_step.trim('both', 'high')
table.insert(steps.pre_justify, trim_high)
end
}
})
<
- Tweak `j` modifier to cycle through available "justify_side" option
values (like in 'junegunn/vim-easy-align'): >lua
require('mini.align').setup({
modifiers = {
j = function(_, opts)
local next_option = ({
left = 'center', center = 'right', right = 'none', none = 'left',
})[opts.justify_side]
opts.justify_side = next_option or 'left'
end,
},
})
<
## Options ~
`MiniAlign.config.options` defines default values of options used to control
behavior of steps.
Examples:
- Set `justify_side = 'center'` to center align at initialization.
For more details about options see |MiniAlign.align_strings()| and entries of
|MiniAlign.gen_step| for default main steps.
## Steps ~
`MiniAlign.config.steps` defines default steps to be applied during
alignment process.
Examples:
- Align by default only first pair of columns: >lua
local align = require('mini.align')
align.setup({
steps = {
pre_justify = { align.gen_step.filter('n == 1') }
},
})
<
------------------------------------------------------------------------------
*MiniAlign.align_strings()*
`MiniAlign.align_strings`({strings}, {opts}, {steps})
Align strings
For details about alignment process see |MiniAlign-algorithm|.
Parameters ~
{strings} `(table)` Array of strings.
{opts} `(table|nil)` Options. Its copy will be passed to steps as second
argument. Extended with `MiniAlign.config.options`.
This is a place to control default main steps:
- `opts.split_pattern` - Lua pattern(s) used to make split parts.
- `opts.split_exclude_patterns` - which split matches should be ignored.
- `opts.justify_side` - which direction(s) alignment should be done.
- `opts.justify_offsets` - offsets tweaking width of first column
- `opts.merge_delimiter` - which delimiter(s) to use when merging.
For more information see |MiniAlign.gen_step| entry for corresponding
default step.
{steps} `(table|nil)` Steps. Extended with `MiniAlign.config.steps`.
Possible `nil` values are replaced with corresponding default steps:
- `split` - |MiniAlign.gen_step.default_split()|.
- `justify` - |MiniAlign.gen_step.default_justify()|.
- `merge` - |MiniAlign.gen_step.default_merge()|.
------------------------------------------------------------------------------
*MiniAlign.align_user()*
`MiniAlign.align_user`({mode})
Align current region with user-supplied steps
Mostly designed to be used inside mappings.
Will use |MiniAlign.align_strings()| and set the following options in `opts`:
- `justify_offsets` - array of offsets used to achieve actual alignment of
a region. It is non-trivial (not array of zeros) only for charwise
selection: offset of first string is computed as width of prefix to the
left of region start.
- `region` - current affected region (see |MiniAlign-glossary|). Can be
used to create more advanced steps.
- `mode` - mode of selection (see |MiniAlign-glossary|).
Parameters ~
{mode} `(string)` Selection mode. One of "char", "line", "block".
------------------------------------------------------------------------------
*MiniAlign.as_parts()*
`MiniAlign.as_parts`({arr2d})
Convert 2d array of strings to parts
This function verifies if input is a proper 2d array of strings and adds
methods to its copy.
Class ~
{parts}
Fields ~
{apply} `(function)` Takes callable `f` and applies it to every part.
Callable should have signature `(s, data)`: `s` is a string part,
`data` - table with its data (<row> has row number, <col> has column number).
Returns new 2d array.
{apply_inplace} `(function)` Takes callable `f` and applies it to every part.
Should have same signature as in `apply` method. Outputs (should all be
strings) are assigned in place to a corresponding parts element. Returns
parts itself to enable method chaining.
{get_dims} `(function)` Return dimensions of parts array: a table with
<row> and <col> keys having number of rows and number of columns (maximum
number of elements across all rows).
{group} `(function)` Concatenate neighboring strings based on supplied
boolean mask and direction (one of "left", default, or "right"). Has
signature `(mask, direction)` and modifies parts in place. Returns parts
itself to enable method chaining.
Example:
- Parts: { { "a", "b", "c" }, { "d", "e" }, { "f" } }
- Mask: { { false, false, true }, { true, false }, { false } }
- Result for direction "left": { { "abc" }, { "d", "e" }, { "f" } }
- Result for direction "right": { { "ab","c" }, { "de" }, { "f" } }
{pair} `(function)` Concatenate neighboring element pairs. Takes
`direction` as input (one of "left", default, or "right") and applies
`group()` for an alternating mask.
Example:
- Parts: { { "a", "b", "c" }, { "d", "e" }, { "f" } }
- Result for direction "left": { { "ab", "c" }, { "de" }, { "f" } }
- Result for direction "right": { { "a", "bc" }, { "de" }, { "f" } }
{slice_col} `(function)` Return column with input index `j`. Note: it might
not be an array if rows have unequal number of columns.
{slice_row} `(function)` Return row with input index `i`.
{trim} `(function)` Trim elements whitespace. Has signature `(direction, indent)`
and modifies parts in place. Returns parts itself to enable method chaining.
- Possible values of `direction`: "both" (default), "left", "right",
"none". Defines from which side whitespaces should be removed.
- Possible values of `indent`: "keep" (default), "low", "high", "remove".
Defines what to do with possible indent (left whitespace of first string
in a row). Value "keep" keeps it; "low" makes all indent equal to the
lowest across rows; "high" - highest across rows; "remove" - removes indent.
Usage ~
>lua
parts = MiniAlign.as_parts({ { 'a', 'b' }, { 'c' } })
print(vim.inspect(parts.get_dims())) -- Should be { row = 2, col = 2 }
parts.apply_inplace(function(s, data)
return ' ' .. data.row .. s .. data.col .. ' '
end)
print(vim.inspect(parts)) -- Should be { { ' 1a1 ', ' 1b2 ' }, { ' 2c1 ' } }
parts.trim('both', 'remove').pair()
print(vim.inspect(parts)) -- Should be { { '1a11b2' }, { '2c1' } }
<
------------------------------------------------------------------------------
*MiniAlign.new_step()*
`MiniAlign.new_step`({name}, {action})
Create step
A step is basically a named callable object. Having a name bundled with
some action powers helper status message during interactive alignment process.
Parameters ~
{name} `(string)` Step name.
{action} `(function|table)` Step action. Should be a callable object
(see |vim.is_callable()|).
Return ~
`(table)` A table with keys: <name> with `name` argument, <action> with `action`.
------------------------------------------------------------------------------
*MiniAlign.gen_step*
`MiniAlign.gen_step`
Generate common action steps
This is a table with function elements. Call to actually get step.
Each step action is a function that has signature `(object, opts)`, where
`object` is either parts or array of strings (depends on which stage of
alignment process it is assumed to be applied) and `opts` is table of options.
Outputs of elements named `default_*` are used as default corresponding main
step (split, justify, merge). Behavior of all of them depend on values from
supplied options (second argument).
Outputs of other elements depend on both step generator input values and
options supplied at execution. This design is mostly because their output
can be used several times in pre-steps.
Usage ~
>lua
local align = require('mini.align')
align.setup({
modifiers = {
-- Use 'T' modifier to remove both whitespace and indent
T = function(steps, _)
table.insert(steps.pre_justify, align.gen_step.trim('both', 'remove'))
end,
},
options = {
-- By default align "right", "left", "right", "left", ...
justify_side = { 'right', 'left' },
},
steps = {
-- Align by default only first pair of columns
pre_justify = { align.gen_step.filter('n == 1') },
},
})
<
------------------------------------------------------------------------------
*MiniAlign.gen_step.default_split()*
`MiniAlign.gen_step.default_split`()
Generate default split step
Output splits strings using matches of Lua pattern(s) from `split_pattern`
option which are not dismissed by `split_exclude_patterns` option.
Outline of how single string is split:
- Convert `split_pattern` option to array of strings (string is converted
as one-element array). This array will be recycled in case there are more
split matches than in converted `split_pattern` array (which almost always).
- Find all forbidden spans (intervals inside string) - all matches of all
patterns in `split_exclude_patterns`.
- Find match for the next pattern. If it is not inside any forbidden span,
add preceding unmatched substring and matched split as two parts. Repeat
with the next pattern.
- If no pattern match is found, add the rest of string as final part.
Output uses following options (as part second argument, `opts` table):
- <split_pattern> - string or array of strings used to detect split matches
and create parts. Default: `''` meaning no matches (whole string is used
as part). Examples: `'%s+'`, `{ '<', '>' }`.
- <split_exclude_patterns> - array of strings defining which regions to
exclude from being matched. Default: `{}`. Examples: `{ '".-"', '^%s*#.*' }`.
Return ~
`(table)` A step named "split" and with appropriate callable action.
See also ~
|MiniAlign.gen_step.ignore_split()| heavily uses `split_exclude_patterns`.
------------------------------------------------------------------------------
*MiniAlign.gen_step.default_justify()*
`MiniAlign.gen_step.default_justify`()
Generate default justify step
Output makes column elements of string parts have equal width by adding
left and/or right whitespace padding. Which side(s) to pad is defined by
`justify_side` option. Width of first column can be tweaked with `justify_offsets`
option.
Outline of how parts are justified:
- Convert `justify_side` option to array of strings (single string is
converted as one-element array). Recycle this array to have length equal
to number of columns in parts.
- For all columns compute maximum width of strings from it (add offsets from
`justify_offsets` to first column widths). Note: for left alignment, width
of last row element does not affect column width. This is mainly because
it won't be padded and helps dealing with "no single match" lines.
- Make all elements have same width inside column by adding appropriate
amount of whitespace. Which side(s) to add is controlled by the corresponding
`justify_side` array element. Note: padding is done with spaces which
might conflict with tab indentation.
Output uses following options (as part second argument, `opts` table):
- <justify_side> - string or array of strings. Each element can be one of
"left" (pad right side), "center" (pad both sides equally), "right" (pad
left side), "none" (no padding). Default: "left".
- <justify_offsets> - array of numeric left offsets of rows. Used to adjust
for possible not equal indents, like in case of charwise selection when
left edge is not on the first column. Default: array of zeros. Set
automatically during interactive alignment in charwise mode.
Return ~
`(table)` A step named "justify" and with appropriate callable action.
------------------------------------------------------------------------------
*MiniAlign.gen_step.default_merge()*
`MiniAlign.gen_step.default_merge`()
Generate default merge step
Output merges rows of parts into strings by placing merge delimiter(s)
between them.
Outline of how parts are converted to array of strings:
- Convert `merge_delimiter` option to array of strings (single string is
converted as one-element array). Recycle this array to have length equal
to number of columns in parts minus 1.
- Exclude empty strings from parts. They add nothing to output except extra
usage of merge delimiter.
- Concatenate each row interleaving with array of merge delimiters.
Output uses following options (as part second argument, `opts` table):
- <merge_delimiter> - string or array of strings. Default: `''`.
Examples: `' '`, `{ '', ' ' }`.
Return ~
`(table)` A step named "merge" and with appropriate callable action.
------------------------------------------------------------------------------
*MiniAlign.gen_step.filter()*
`MiniAlign.gen_step.filter`({expr})
Generate filter step
Construct function predicate from supplied Lua string expression and make
step evaluating it on every part element.
Outline of how filtering is done:
- Convert Lua filtering expression into function predicate which can be
evaluated in manually created context (some specific variables being set).
- Compute boolean mask for parts by applying predicate to each element of
2d array with special variables set to specific values (see next section).
- Group parts with compted mask. See `group()` method of parts in
|MiniAlign.as_parts()|.
Special variables which can be used in expression:
- <row> - row number of current element.
- <ROW> - total number of rows in parts.
- <col> - column number of current element.
- <COL> - total number of columns in current row.
- <s> - string value of current element.
- <n> - column pair number of current element. Useful when filtering by
result of pattern splitting.
- <N> - total number of column pairs in current row.
- All variables from global table `_G`.
Tips:
- This general filtering approach can be used to both include and exclude
certain parts from alignment. Examples:
- Use `row ~= 2` to align all parts except from second row.
- Use `n == 1` to align only by first pair of columns.
- Filtering by last equal sign usually can be done with `n >= (N - 1)`
(because there is usually something to the right of it).
Parameters ~
{expr} `(string)` Lua expression as a string which will be used as predicate.
Return ~
`(table|nil)` A step named "filter" and with appropriate callable action.
------------------------------------------------------------------------------
*MiniAlign.gen_step.ignore_split()*
`MiniAlign.gen_step.ignore_split`({patterns}, {exclude_comment})
Generate ignore step
Output adds certain values to `split_exclude_patterns` option. Should be
used as pre-split step.
Parameters ~
{patterns} `(table)` Array of patterns to be added to
`split_exclude_patterns` as is. Default: `{ [[".-"]] }` (excludes strings
for most cases).
{exclude_comment} `(boolean|nil)` Whether to add comment pattern to
`split_exclude_patterns`. Comment pattern is derived from 'commentstring'
option. Default: `true`.
Return ~
`(table)` A step named "ignore" and with appropriate callable action.
See also ~
|MiniAlign.gen_step.default_split()| for details about
`split_exclude_patterns` option.
------------------------------------------------------------------------------
*MiniAlign.gen_step.pair()*
`MiniAlign.gen_step.pair`({direction})
Generate pair step
Output calls `pair()` method of parts (see |MiniAlign.as_parts()|) with
supplied `direction` argument.
Parameters ~
{direction} `(string)` Which direction to pair. One of "left" (default) or
Return ~
`(table)` A step named "pair" and with appropriate callable action.
------------------------------------------------------------------------------
*MiniAlign.gen_step.trim()*
`MiniAlign.gen_step.trim`({direction}, {indent})
Generate trim step
Output calls `trim()` method of parts (see |MiniAlign.as_parts()|) with
supplied `direction` and `indent` arguments.
Parameters ~
{direction} `(string|nil)` Which sides to trim whitespace. One of "both"
(default), "left", "right", "none".
{indent} `(string|nil)` What to do with possible indent (left whitespace
of first string in a row). One of "keep" (default), "low", "high", "remove".
Return ~
`(table)` A step named "trim" and with appropriate callable action.
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -0,0 +1,938 @@
*mini.animate* Animate common Neovim actions
*MiniAnimate*
MIT License Copyright (c) 2022 Evgeni Chasnovski
==============================================================================
Features:
- Works out of the box with a single `require('mini.animate').setup()`.
No extra mappings or commands needed.
- Animate cursor movement inside same buffer by showing customizable path.
See |MiniAnimate.config.cursor| for more details.
- Animate scrolling with a series of subscrolls ("smooth scrolling").
See |MiniAnimate.config.scroll| for more details.
- Animate window resize by gradually changing sizes of all windows.
See |MiniAnimate.config.resize| for more details.
- Animate window open/close with visually updating floating window.
See |MiniAnimate.config.open| and |MiniAnimate.config.close| for more details.
- Timings for all actions can be customized independently.
See |MiniAnimate-timing| for more details.
- Action animations can be enabled/disabled independently.
- All animations are asynchronous/non-blocking and trigger a targeted event
which can be used to perform actions after animation is done.
- |MiniAnimate.animate()| function which can be used to perform own animations.
Notes:
- Cursor movement is animated inside same window and buffer, not as cursor
moves across the screen.
- Scroll and resize animations are done with "side effects": they actually
change the state of what is animated (window view and sizes
respectively). This has a downside of possibly needing extra work to
account for asynchronous nature of animation (like adjusting certain
mappings, etc.). See |MiniAnimate.config.scroll| and
|MiniAnimate.config.resize| for more details.
- Although all animations work in all supported versions of Neovim, scroll
and resize animations have best experience with Neovim>=0.9. This is due
to updated implementation of |WinScrolled| event.
# Setup ~
This module needs a setup with `require('mini.animate').setup({})` (replace
`{}` with your `config` table). It will create global Lua table `MiniAnimate`
which you can use for scripting or manually (with `:lua MiniAnimate.*`).
See |MiniAnimate.config| for available config settings.
You can override runtime config settings (like `config.modifiers`) locally
to buffer inside `vim.b.minianimate_config` which should have same structure
as `MiniAnimate.config`. See |mini.nvim-buffer-local-config| for more details.
# Comparisons ~
- Neovide:
- Neovide is a standalone GUI which has more control over its animations.
While 'mini.animate' works inside terminal emulator (with all its
limitations, like lack of pixel-size control over animations).
- Neovide animates cursor movement across screen, while 'mini.animate' -
as it moves across same buffer.
- Neovide has fixed number of animation effects per action, while
'mini.animate' is fully customizable.
- 'mini.animate' implements animations for window open/close, while
Neovide does not.
- 'edluffy/specs.nvim':
- 'mini.animate' approaches cursor movement visualization via
customizable path function (uses extmarks), while 'specs.nvim' can
customize within its own visual effects (shading and floating
window resizing).
- 'karb94/neoscroll.nvim':
- Scroll animation is triggered only inside dedicated mappings.
'mini.animate' animates scroll resulting from any window view change.
- 'anuvyklack/windows.nvim':
- Resize animation is done only within custom commands and mappings,
while 'mini.animate' animates any resize out of the box (works
similarly to 'windows.nvim' in Neovim>=0.9 with appropriate
'winheight' / 'winwidth' and 'winminheight' / 'winminwidth').
# Highlight groups ~
* `MiniAnimateCursor` - highlight of cursor during its animated movement.
* `MiniAnimateNormalFloat` - highlight of floating window for `open` and
`close` animations.
To change any highlight group, modify it directly with |:highlight|.
# Disabling ~
To disable, set `vim.g.minianimate_disable` (globally) or
`vim.b.minianimate_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.
------------------------------------------------------------------------------
*MiniAnimate.setup()*
`MiniAnimate.setup`({config})
Module setup
Parameters ~
{config} `(table|nil)` Module config table. See |MiniAnimate.config|.
Usage ~
>lua
require('mini.animate').setup() -- use default config
-- OR
require('mini.animate').setup({}) -- replace {} with your config table
<
------------------------------------------------------------------------------
*MiniAnimate.config*
`MiniAnimate.config`
Module config
Default values:
>lua
MiniAnimate.config = {
-- Cursor path
cursor = {
-- Whether to enable this animation
enable = true,
-- Timing of animation (how steps will progress in time)
timing = --<function: implements linear total 250ms animation duration>,
-- Path generator for visualized cursor movement
path = --<function: implements shortest line path>,
},
-- Vertical scroll
scroll = {
-- Whether to enable this animation
enable = true,
-- Timing of animation (how steps will progress in time)
timing = --<function: implements linear total 250ms animation duration>,
-- Subscroll generator based on total scroll
subscroll = --<function: implements equal scroll with at most 60 steps>,
},
-- Window resize
resize = {
-- Whether to enable this animation
enable = true,
-- Timing of animation (how steps will progress in time)
timing = --<function: implements linear total 250ms animation duration>,
-- Subresize generator for all steps of resize animations
subresize = --<function: implements equal linear steps>,
},
-- Window open
open = {
-- Whether to enable this animation
enable = true,
-- Timing of animation (how steps will progress in time)
timing = --<function: implements linear total 250ms animation duration>,
-- Floating window config generator visualizing specific window
winconfig = --<function: implements static window for 25 steps>,
-- 'winblend' (window transparency) generator for floating window
winblend = --<function: implements equal linear steps from 80 to 100>,
},
-- Window close
close = {
-- Whether to enable this animation
enable = true,
-- Timing of animation (how steps will progress in time)
timing = --<function: implements linear total 250ms animation duration>,
-- Floating window config generator visualizing specific window
winconfig = --<function: implements static window for 25 steps>,
-- 'winblend' (window transparency) generator for floating window
winblend = --<function: implements equal linear steps from 80 to 100>,
},
}
<
# General ~
*MiniAnimate-timing*
- Every animation is a non-blockingly scheduled series of specific actions.
They are executed in a sequence of timed steps controlled by `timing` option.
It is a callable which, given next and total step numbers, returns wait time
(in ms). See |MiniAnimate.gen_timing| for builtin timing functions.
See |MiniAnimate.animate()| for more details about animation process.
- Every animation can be enabled/disabled independently by setting `enable`
option to `true`/`false`.
*MiniAnimate-done-event*
- Every animation triggers custom |User| event when it is finished. It is
named `MiniAnimateDoneXxx` with `Xxx` replaced by capitalized supported
animation action name (like `MiniAnimateDoneCursor`). Use it to schedule
some action after certain animation is completed. Alternatively, you can
use |MiniAnimate.execute_after()| (usually preferred in mappings).
- Each animation has its main step generator which defines how particular
animation is done. They all are callables which take some input data and
return an array of step data. Length of that array determines number of
animation steps. Outputs `nil` and empty table result in no animation.
*MiniAnimate.config.cursor*
# Cursor ~
This animation is triggered for each movement of cursor inside same window
and buffer. Its visualization step consists from placing single extmark (see
|extmarks|) at certain position. This extmark contains single space and is
highlighted with `MiniAnimateCursor` highlight group.
Exact places of extmark and their number is controlled by `path` option. It
is a callable which takes `destination` argument (2d integer point in
`(line, col)` coordinates) and returns array of relative to `(0, 0)` places
for extmark to be placed. Example:
- Input `(2, -3)` means cursor jumped 2 lines forward and 3 columns backward.
- Output `{ {0, 0 }, { 0, -1 }, { 0, -2 }, { 0, -3 }, { 1, -3 } }` means
that path is first visualized along the initial line and then along final
column.
See |MiniAnimate.gen_path| for builtin path generators.
Notes:
- Input `destination` value is computed ignoring folds. This is by design
as it helps better visualize distance between two cursor positions.
- Outputs of path generator resulting in a place where extmark can't be
placed are silently omitted during animation: this step won't show any
visualization.
Configuration example: >lua
local animate = require('mini.animate')
animate.setup({
cursor = {
-- Animate for 200 milliseconds with linear easing
timing = animate.gen_timing.linear({ duration = 200, unit = 'total' }),
-- Animate with shortest line for any cursor move
path = animate.gen_path.line({
predicate = function() return true end,
}),
}
})
<
After animation is done, `MiniAnimateDoneCursor` event is triggered.
*MiniAnimate.config.scroll*
# Scroll ~
This animation is triggered for each vertical scroll of current window.
Its visualization step consists from performing a small subscroll which all
in total will result into needed total scroll.
Exact subscroll values and their number is controlled by `subscroll` option.
It is a callable which takes `total_scroll` argument (single non-negative
integer) and returns array of non-negative integers each representing the
amount of lines needed to be scrolled inside corresponding step. All
subscroll values should sum to input `total_scroll`.
Example:
- Input `5` means that total scroll consists from 5 lines (either up or down,
which doesn't matter).
- Output of `{ 1, 1, 1, 1, 1 }` means that there are 5 equal subscrolls.
See |MiniAnimate.gen_subscroll| for builtin subscroll generators.
Notes:
- Input value of `total_scroll` is computed taking folds into account.
- As scroll animation is essentially a precisely scheduled non-blocking
subscrolls, this has two important interconnected consequences:
- If another scroll is attempted during the animation, it is done based
on the **currently visible** window view. Example: if user presses
|CTRL-D| and then |CTRL-U| when animation is half done, window will not
display the previous view half of 'scroll' above it. This especially
affects mouse wheel scrolling, as each its turn results in a new scroll
for number of lines defined by 'mousescroll'. Tweak it to your liking.
- It breaks the use of several relative scrolling commands in the same
command. Use |MiniAnimate.execute_after()| to schedule action after
reaching target window view.
Example: a useful `nnoremap n nzvzz` mapping (consecutive application
of |n|, |zv|, and |zz|) should be expressed in the following way: >lua
'<Cmd>lua vim.cmd("normal! n"); ' ..
'MiniAnimate.execute_after("scroll", "normal! zvzz")<CR>'
<
- This animation works best with Neovim>=0.9 (after certain updates to
|WinScrolled| event).
Configuration example: >lua
local animate = require('mini.animate')
animate.setup({
scroll = {
-- Animate for 200 milliseconds with linear easing
timing = animate.gen_timing.linear({ duration = 200, unit = 'total' }),
-- Animate equally but with at most 120 steps instead of default 60
subscroll = animate.gen_subscroll.equal({ max_output_steps = 120 }),
}
})
<
After animation is done, `MiniAnimateDoneScroll` event is triggered.
*MiniAnimate.config.resize*
# Resize ~
This animation is triggered for window resize while having same layout of
same windows. For example, it won't trigger when window is opened/closed or
after something like |CTRL-W_K|. Its visualization step consists from setting
certain sizes to all visible windows (last step being for "true" final sizes).
Exact window step sizes and their number is controlled by `subresize` option.
It is a callable which takes `sizes_from` and `sizes_to` arguments (both
tables with window id as keys and dimension table as values) and returns
array of same shaped data.
Example:
- Input: >lua
-- First
{ [1000] = {width = 7, height = 5}, [1001] = {width = 7, height = 10} }
-- Second
{ [1000] = {width = 9, height = 5}, [1001] = {width = 5, height = 10} }
-- Means window 1000 increased its width by 2 in expense of window 1001
<
- The following output demonstrates equal resizing: >lua
{
{ [1000] = {width = 8, height = 5}, [1001] = {width = 6, height = 10} },
{ [1000] = {width = 9, height = 5}, [1001] = {width = 5, height = 10} },
}
<
See |MiniAnimate.gen_subresize| for builtin subresize generators.
Notes:
- As resize animation is essentially a precisely scheduled non-blocking
subresizes, this has two important interconnected consequences:
- If another resize is attempted during the animation, it is done based
on the **currently visible** window sizes. This might affect relative
resizing.
- It breaks the use of several relative resizing commands in the same
command. Use |MiniAnimate.execute_after()| to schedule action after
reaching target window sizes.
- This animation works best with Neovim>=0.9 (after certain updates to
|WinScrolled| event). For example, resize resulting from effect of
'winheight' / 'winwidth' will work properly.
Configuration example: >lua
local is_many_wins = function(sizes_from, sizes_to)
return vim.tbl_count(sizes_from) >= 3
end
local animate = require('mini.animate')
animate.setup({
resize = {
-- Animate for 200 milliseconds with linear easing
timing = animate.gen_timing.linear({ duration = 200, unit = 'total' }),
-- Animate only if there are at least 3 windows
subresize = animate.gen_subscroll.equal({ predicate = is_many_wins }),
}
})
<
After animation is done, `MiniAnimateDoneResize` event is triggered.
*MiniAnimate.config.open* *MiniAnimate.config.close*
# Window open/close ~
These animations are similarly triggered for regular (non-floating) window
open/close. Their visualization step consists from drawing empty floating
window with customizable config and transparency.
Exact window visualization characteristics are controlled by `winconfig`
and `winblend` options.
The `winconfig` option is a callable which takes window id (|window-ID|) as
input and returns an array of floating window configs (as in `config`
argument of |nvim_open_win()|). Its length determines number of animation steps.
Example:
- The following output results into two animation steps with second being
upper left quarter of a first: >lua
{
{
row = 0, col = 0,
width = 10, height = 10,
relative = 'editor', anchor = 'NW', focusable = false,
zindex = 1, style = 'minimal',
},
{
row = 0, col = 0,
width = 5, height = 5,
relative = 'editor', anchor = 'NW', focusable = false,
zindex = 1, style = 'minimal',
},
}
<
The `winblend` option is similar to `timing` option: it is a callable
which, given current and total step numbers, returns value of floating
window's 'winblend' option. Note, that it is called for current step (so
starts from 0), as opposed to `timing` which is called before step.
Example:
- Function `function(s, n) return 80 + 20 * s / n end` results in linear
transition from `winblend` value of 80 to 100.
See |MiniAnimate.gen_winconfig| for builtin window config generators.
See |MiniAnimate.gen_winblend| for builtin window transparency generators.
Configuration example: >lua
local animate = require('mini.animate')
animate.setup({
open = {
-- Animate for 400 milliseconds with linear easing
timing = animate.gen_timing.linear({ duration = 400, unit = 'total' }),
-- Animate with wiping from nearest edge instead of default static one
winconfig = animate.gen_winconfig.wipe({ direction = 'from_edge' }),
-- Make bigger windows more transparent
winblend = animate.gen_winblend.linear({ from = 80, to = 100 }),
},
close = {
-- Animate for 400 milliseconds with linear easing
timing = animate.gen_timing.linear({ duration = 400, unit = 'total' }),
-- Animate with wiping to nearest edge instead of default static one
winconfig = animate.gen_winconfig.wipe({ direction = 'to_edge' }),
-- Make bigger windows more transparent
winblend = animate.gen_winblend.linear({ from = 100, to = 80 }),
},
})
<
After animation is done, `MiniAnimateDoneOpen` or `MiniAnimateDoneClose`
event is triggered for `open` and `close` animation respectively.
------------------------------------------------------------------------------
*MiniAnimate.is_active()*
`MiniAnimate.is_active`({animation_type})
Check animation activity
Parameters ~
{animation_type} `(string)` One of supported animation types
(entries of |MiniAnimate.config|, like `'cursor'`, etc.).
Return ~
`(boolean)` Whether the animation is currently active.
------------------------------------------------------------------------------
*MiniAnimate.execute_after()*
`MiniAnimate.execute_after`({animation_type}, {action})
Execute action after some animation is done
Execute action immediately if animation is not active (checked with
|MiniAnimate.is_active()|). Else, schedule its execution until after
animation is done (on corresponding "done event", see
|MiniAnimate-done-event|).
Mostly meant to be used inside mappings.
Example ~
A useful `nnoremap n nzvzz` mapping (consecutive application of |n|, |zv|, and |zz|)
should be expressed in the following way: >lua
'<Cmd>lua vim.cmd("normal! n"); ' ..
'MiniAnimate.execute_after("scroll", "normal! zvzz")<CR>'
<
Parameters ~
{animation_type} `(string)` One of supported animation types
(as in |MiniAnimate.is_active()|).
{action} `(string|function)` Action to be executed. If string, executed as
command (via |vim.cmd()|).
------------------------------------------------------------------------------
*MiniAnimate.animate()*
`MiniAnimate.animate`({step_action}, {step_timing}, {opts})
Animate action
This is equivalent to asynchronous execution of the following algorithm:
- Call `step_action(0)` immediately after calling this function. Stop if
action returned `false` or `nil`.
- Wait `step_timing(1)` milliseconds.
- Call `step_action(1)`. Stop if it returned `false` or `nil`.
- Wait `step_timing(2)` milliseconds.
- Call `step_action(2)`. Stop if it returned `false` or `nil`.
- ...
Notes:
- Animation is also stopped on action error or if maximum number of steps
is reached.
- Asynchronous execution is done with |uv.new_timer()|. It only allows
integer parts as repeat value. This has several implications:
- Outputs of `step_timing()` are accumulated in order to preserve total
execution time.
- Any wait time less than 1 ms means that action will be executed
immediately.
Parameters ~
{step_action} `(function|table)` Callable which takes `step` (integer 0, 1, 2,
etc. indicating current step) and executes some action. Its return value
defines when animation should stop: values `false` and `nil` (equivalent
to no explicit return) stop animation timer; any other continues it.
{step_timing} `(function|table)` Callable which takes `step` (integer 1, 2, etc.
indicating next step) and returns how many milliseconds to wait before
executing this step action.
{opts} `(table|nil)` Options. Possible fields:
- <max_steps> - Maximum value of allowed step to execute. Default: 10000000.
------------------------------------------------------------------------------
*MiniAnimate.gen_timing*
`MiniAnimate.gen_timing`
Generate animation timing
Each field corresponds to one family of progression which can be customized
further by supplying appropriate arguments.
This is a table with function elements. Call to actually get timing function.
Example: >lua
local animate = require('mini.animate')
animate.setup({
cursor = {
timing = animate.gen_timing.linear({ duration = 100, unit = 'total' })
},
})
<
See also ~
|MiniIndentscope.gen_animation| for similar concept in 'mini.indentscope'.
------------------------------------------------------------------------------
*MiniAnimate.gen_timing.none()*
`MiniAnimate.gen_timing.none`()
Generate timing with no animation
Show final result immediately. Usually better to use `enable` field in `config`
if you want to disable animation.
------------------------------------------------------------------------------
*MiniAnimate.gen_timing.linear()*
`MiniAnimate.gen_timing.linear`({opts})
Generate timing with linear progression
Parameters ~
{opts} `(table|nil)` Options that control progression. Possible keys:
- <easing> `(string)` - a subtype of progression. One of "in"
(accelerating from zero speed), "out" (decelerating to zero speed),
"in-out" (default; accelerating halfway, decelerating after).
- <duration> `(number)` - duration (in ms) of a unit. Default: 20.
- <unit> `(string)` - which unit's duration `opts.duration` controls. One
of "step" (default; ensures average duration of step to be `opts.duration`)
or "total" (ensures fixed total duration regardless of scope's range).
Return ~
`(function)` Timing function (see |MiniAnimate-timing|).
------------------------------------------------------------------------------
*MiniAnimate.gen_timing.quadratic()*
`MiniAnimate.gen_timing.quadratic`({opts})
Generate timing with quadratic progression
Parameters ~
{opts} `(table|nil)` Options that control progression. Possible keys:
- <easing> `(string)` - a subtype of progression. One of "in"
(accelerating from zero speed), "out" (decelerating to zero speed),
"in-out" (default; accelerating halfway, decelerating after).
- <duration> `(number)` - duration (in ms) of a unit. Default: 20.
- <unit> `(string)` - which unit's duration `opts.duration` controls. One
of "step" (default; ensures average duration of step to be `opts.duration`)
or "total" (ensures fixed total duration regardless of scope's range).
Return ~
`(function)` Timing function (see |MiniAnimate-timing|).
------------------------------------------------------------------------------
*MiniAnimate.gen_timing.cubic()*
`MiniAnimate.gen_timing.cubic`({opts})
Generate timing with cubic progression
Parameters ~
{opts} `(table|nil)` Options that control progression. Possible keys:
- <easing> `(string)` - a subtype of progression. One of "in"
(accelerating from zero speed), "out" (decelerating to zero speed),
"in-out" (default; accelerating halfway, decelerating after).
- <duration> `(number)` - duration (in ms) of a unit. Default: 20.
- <unit> `(string)` - which unit's duration `opts.duration` controls. One
of "step" (default; ensures average duration of step to be `opts.duration`)
or "total" (ensures fixed total duration regardless of scope's range).
Return ~
`(function)` Timing function (see |MiniAnimate-timing|).
------------------------------------------------------------------------------
*MiniAnimate.gen_timing.quartic()*
`MiniAnimate.gen_timing.quartic`({opts})
Generate timing with quartic progression
Parameters ~
{opts} `(table|nil)` Options that control progression. Possible keys:
- <easing> `(string)` - a subtype of progression. One of "in"
(accelerating from zero speed), "out" (decelerating to zero speed),
"in-out" (default; accelerating halfway, decelerating after).
- <duration> `(number)` - duration (in ms) of a unit. Default: 20.
- <unit> `(string)` - which unit's duration `opts.duration` controls. One
of "step" (default; ensures average duration of step to be `opts.duration`)
or "total" (ensures fixed total duration regardless of scope's range).
Return ~
`(function)` Timing function (see |MiniAnimate-timing|).
------------------------------------------------------------------------------
*MiniAnimate.gen_timing.exponential()*
`MiniAnimate.gen_timing.exponential`({opts})
Generate timing with exponential progression
Parameters ~
{opts} `(table|nil)` Options that control progression. Possible keys:
- <easing> `(string)` - a subtype of progression. One of "in"
(accelerating from zero speed), "out" (decelerating to zero speed),
"in-out" (default; accelerating halfway, decelerating after).
- <duration> `(number)` - duration (in ms) of a unit. Default: 20.
- <unit> `(string)` - which unit's duration `opts.duration` controls. One
of "step" (default; ensures average duration of step to be `opts.duration`)
or "total" (ensures fixed total duration regardless of scope's range).
Return ~
`(function)` Timing function (see |MiniAnimate-timing|).
------------------------------------------------------------------------------
*MiniAnimate.gen_path*
`MiniAnimate.gen_path`
Generate cursor animation path
For more information see |MiniAnimate.config.cursor|.
This is a table with function elements. Call to actually get generator.
Example: >lua
local animate = require('mini.animate')
animate.setup({
cursor = {
-- Animate with line-column angle instead of shortest line
path = animate.gen_path.angle(),
}
})
<
------------------------------------------------------------------------------
*MiniAnimate.gen_path.line()*
`MiniAnimate.gen_path.line`({opts})
Generate path as shortest line
Parameters ~
{opts} `(table|nil)` Options that control generator. Possible keys:
- <predicate> `(function)` - a callable which takes `destination` as input and
returns boolean value indicating whether animation should be done.
Default: `false` if `destination` is within one line of origin (reduces
flickering), `true` otherwise.
Return ~
`(function)` Path function (see |MiniAnimate.config.cursor|).
------------------------------------------------------------------------------
*MiniAnimate.gen_path.angle()*
`MiniAnimate.gen_path.angle`({opts})
Generate path as line/column angle
Parameters ~
{opts} `(table|nil)` Options that control generator. Possible keys:
- <predicate> `(function)` - a callable which takes `destination` as input and
returns boolean value indicating whether animation should be done.
Default: `false` if `destination` is within one line of origin (reduces
flickering), `true` otherwise.
- <first_direction> `(string)` - one of `"horizontal"` (default; animates
across initial line first) or `"vertical"` (animates across initial
column first).
Return ~
`(function)` Path function (see |MiniAnimate.config.cursor|).
------------------------------------------------------------------------------
*MiniAnimate.gen_path.walls()*
`MiniAnimate.gen_path.walls`({opts})
Generate path as closing walls at final position
Parameters ~
{opts} `(table|nil)` Options that control generator. Possible keys:
- <predicate> `(function)` - a callable which takes `destination` as input and
returns boolean value indicating whether animation should be done.
Default: `false` if `destination` is within one line of origin (reduces
flickering), `true` otherwise.
- <width> `(number)` - initial width of left and right walls. Default: 10.
Return ~
`(function)` Path function (see |MiniAnimate.config.cursor|).
------------------------------------------------------------------------------
*MiniAnimate.gen_path.spiral()*
`MiniAnimate.gen_path.spiral`({opts})
Generate path as diminishing spiral at final position
Parameters ~
{opts} `(table|nil)` Options that control generator. Possible keys:
- <predicate> `(function)` - a callable which takes `destination` as input and
returns boolean value indicating whether animation should be done.
Default: `false` if `destination` is within one line of origin (reduces
flickering), `true` otherwise.
- <width> `(number)` - initial width of spiral. Default: 2.
Return ~
`(function)` Path function (see |MiniAnimate.config.cursor|).
------------------------------------------------------------------------------
*MiniAnimate.gen_subscroll*
`MiniAnimate.gen_subscroll`
Generate scroll animation subscroll
For more information see |MiniAnimate.config.scroll|.
This is a table with function elements. Call to actually get generator.
Example: >lua
local animate = require('mini.animate')
animate.setup({
scroll = {
-- Animate equally but with 120 maximum steps instead of default 60
subscroll = animate.gen_subscroll.equal({ max_output_steps = 120 }),
}
})
<
------------------------------------------------------------------------------
*MiniAnimate.gen_subscroll.equal()*
`MiniAnimate.gen_subscroll.equal`({opts})
Generate subscroll with equal steps
Parameters ~
{opts} `(table|nil)` Options that control generator. Possible keys:
- <predicate> `(function)` - a callable which takes `total_scroll` as
input and returns boolean value indicating whether animation should be
done. Default: `false` if `total_scroll` is 1 or less (reduces
unnecessary waiting), `true` otherwise.
- <max_output_steps> `(number)` - maximum number of subscroll steps in output.
Adjust this to reduce computations in expense of reduced smoothness.
Default: 60.
Return ~
`(function)` Subscroll function (see |MiniAnimate.config.scroll|).
------------------------------------------------------------------------------
*MiniAnimate.gen_subresize*
`MiniAnimate.gen_subresize`
Generate resize animation subresize
For more information see |MiniAnimate.config.resize|.
This is a table with function elements. Call to actually get generator.
Example: >lua
local is_many_wins = function(sizes_from, sizes_to)
return vim.tbl_count(sizes_from) >= 3
end
local animate = require('mini.animate')
animate.setup({
resize = {
-- Animate only if there are at least 3 windows
subresize = animate.gen_subresize.equal({ predicate = is_many_wins }),
}
})
<
------------------------------------------------------------------------------
*MiniAnimate.gen_subresize.equal()*
`MiniAnimate.gen_subresize.equal`({opts})
Generate subresize with equal steps
Parameters ~
{opts} `(table|nil)` Options that control generator. Possible keys:
- <predicate> `(function)` - a callable which takes `sizes_from` and
`sizes_to` as input and returns boolean value indicating whether
animation should be done. Default: always `true`.
Return ~
`(function)` Subresize function (see |MiniAnimate.config.resize|).
------------------------------------------------------------------------------
*MiniAnimate.gen_winconfig*
`MiniAnimate.gen_winconfig`
Generate open/close animation winconfig
For more information see |MiniAnimate.config.open| or |MiniAnimate.config.close|.
This is a table with function elements. Call to actually get generator.
Example: >lua
local is_not_single_window = function(win_id)
local tabpage_id = vim.api.nvim_win_get_tabpage(win_id)
return #vim.api.nvim_tabpage_list_wins(tabpage_id) > 1
end
local animate = require('mini.animate')
animate.setup({
open = {
-- Animate with wiping from nearest edge instead of default static one
-- and only if it is not a single window in tabpage
winconfig = animate.gen_winconfig.wipe({
predicate = is_not_single_window,
direction = 'from_edge',
}),
},
close = {
-- Animate with wiping to nearest edge instead of default static one
-- and only if it is not a single window in tabpage
winconfig = animate.gen_winconfig.wipe({
predicate = is_not_single_window,
direction = 'to_edge',
}),
},
})
<
------------------------------------------------------------------------------
*MiniAnimate.gen_winconfig.static()*
`MiniAnimate.gen_winconfig.static`({opts})
Generate winconfig for static floating window
This will result into floating window statically covering whole target
window.
Parameters ~
{opts} `(table|nil)` Options that control generator. Possible keys:
- <predicate> `(function)` - a callable which takes `win_id` as input and
returns boolean value indicating whether animation should be done.
Default: always `true`.
- <n_steps> `(number)` - number of output steps, all with same config.
Useful to tweak smoothness of transparency animation (done inside
`winblend` config option). Default: 25.
Return ~
`(function)` Winconfig function (see |MiniAnimate.config.open|
or |MiniAnimate.config.close|).
------------------------------------------------------------------------------
*MiniAnimate.gen_winconfig.center()*
`MiniAnimate.gen_winconfig.center`({opts})
Generate winconfig for center-focused animated floating window
This will result into floating window growing from or shrinking to the
target window center.
Parameters ~
{opts} `(table|nil)` Options that control generator. Possible keys:
- <predicate> `(function)` - a callable which takes `win_id` as input and
returns boolean value indicating whether animation should be done.
Default: always `true`.
- <direction> `(string)` - one of `"to_center"` (default; window will
shrink from full coverage to center) or `"from_center"` (window will
grow from center to full coverage).
Return ~
`(function)` Winconfig function (see |MiniAnimate.config.open|
or |MiniAnimate.config.close|).
------------------------------------------------------------------------------
*MiniAnimate.gen_winconfig.wipe()*
`MiniAnimate.gen_winconfig.wipe`({opts})
Generate winconfig for wiping animated floating window
This will result into floating window growing from or shrinking to the
nearest edge. This also takes into account the split type of target window:
vertically split window will progress towards vertical edge; horizontally -
towards horizontal.
Parameters ~
{opts} `(table|nil)` Options that control generator. Possible keys:
- <predicate> `(function)` - a callable which takes `win_id` as input and
returns boolean value indicating whether animation should be done.
Default: always `true`.
- <direction> `(string)` - one of `"to_edge"` (default; window will
shrink from full coverage to nearest edge) or `"from_edge"` (window
will grow from edge to full coverage).
Return ~
`(function)` Winconfig function (see |MiniAnimate.config.open|
or |MiniAnimate.config.close|).
------------------------------------------------------------------------------
*MiniAnimate.gen_winblend*
`MiniAnimate.gen_winblend`
Generate open/close animation `winblend` progression
For more information see |MiniAnimate.config.open| or |MiniAnimate.config.close|.
This is a table with function elements. Call to actually get transparency
function.
Example: >lua
local animate = require('mini.animate')
animate.setup({
open = {
-- Change transparency from 60 to 80 instead of default 80 to 100
winblend = animate.gen_winblend.linear({ from = 60, to = 80 }),
},
close = {
-- Change transparency from 60 to 80 instead of default 80 to 100
winblend = animate.gen_winblend.linear({ from = 60, to = 80 }),
},
})
<
------------------------------------------------------------------------------
*MiniAnimate.gen_winblend.linear()*
`MiniAnimate.gen_winblend.linear`({opts})
Generate linear `winblend` progression
Parameters ~
{opts} `(table|nil)` Options that control generator. Possible keys:
- <from> `(number)` - initial value of 'winblend'.
- <to> `(number)` - final value of 'winblend'.
Return ~
`(function)` Winblend function (see |MiniAnimate.config.open|
or |MiniAnimate.config.close|).
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -0,0 +1,282 @@
*mini.base16* Base16 colorscheme creation
*MiniBase16*
MIT License Copyright (c) 2021 Evgeni Chasnovski
==============================================================================
Fast implementation of 'chriskempson/base16' color scheme (with Copyright
(C) 2012 Chris Kempson) adapted for modern Neovim Lua plugins.
Extra features:
- Configurable automatic support of cterm colors (see |highlight-cterm|).
- Opinionated palette generator based only on background and foreground
colors.
Supported highlight groups:
- Built-in Neovim LSP and diagnostic.
- Plugins (either with explicit definition or by verification that default
highlighting works appropriately):
- 'echasnovski/mini.nvim'
- 'akinsho/bufferline.nvim'
- 'anuvyklack/hydra.nvim'
- 'DanilaMihailov/beacon.nvim'
- 'folke/lazy.nvim'
- 'folke/noice.nvim'
- 'folke/todo-comments.nvim'
- 'folke/trouble.nvim'
- 'folke/which-key.nvim'
- 'ggandor/leap.nvim'
- 'ggandor/lightspeed.nvim'
- 'glepnir/dashboard-nvim'
- 'glepnir/lspsaga.nvim'
- 'HiPhish/rainbow-delimiters.nvim'
- 'hrsh7th/nvim-cmp'
- 'justinmk/vim-sneak'
- 'kevinhwang91/nvim-ufo'
- 'lewis6991/gitsigns.nvim'
- 'lukas-reineke/indent-blankline.nvim'
- 'neoclide/coc.nvim'
- 'NeogitOrg/neogit'
- 'nvim-lualine/lualine.nvim'
- 'nvim-neo-tree/neo-tree.nvim'
- 'nvim-telescope/telescope.nvim'
- 'nvim-tree/nvim-tree.lua'
- 'phaazon/hop.nvim'
- 'rcarriga/nvim-dap-ui'
- 'rcarriga/nvim-notify'
- 'rlane/pounce.nvim'
- 'romgrk/barbar.nvim'
- 'stevearc/aerial.nvim'
- 'williamboman/mason.nvim'
# Setup ~
This module needs a setup with `require('mini.base16').setup({})` (replace
`{}` with your `config` table). It will create global Lua table
`MiniBase16` which you can use for scripting or manually (with
`:lua MiniBase16.*`).
See |MiniBase16.config| for `config` structure and default values.
This module doesn't have runtime options, so using `vim.b.minibase16_config`
will have no effect here.
Example: >lua
require('mini.base16').setup({
palette = {
base00 = '#112641',
base01 = '#3a475e',
base02 = '#606b81',
base03 = '#8691a7',
base04 = '#d5dc81',
base05 = '#e2e98f',
base06 = '#eff69c',
base07 = '#fcffaa',
base08 = '#ffcfa0',
base09 = '#cc7e46',
base0A = '#46a436',
base0B = '#9ff895',
base0C = '#ca6ecf',
base0D = '#42f7ff',
base0E = '#ffc4ff',
base0F = '#00a5c5',
},
use_cterm = true,
plugins = {
default = false,
['echasnovski/mini.nvim'] = true,
},
})
<
# Notes ~
1. This is used to create plugin's colorschemes (see |mini.nvim-color-schemes|).
2. Using `setup()` doesn't actually create a |colorscheme|. It basically
creates a coordinated set of |highlight|s. To create your own theme:
- Put "myscheme.lua" file (name after your chosen theme name) inside
any "colors" directory reachable from 'runtimepath' ("colors" inside
your Neovim config directory is usually enough).
- Inside "myscheme.lua" call `require('mini.base16').setup()` with your
palette and only after that set |g:colors_name| to "myscheme".
------------------------------------------------------------------------------
*mini-base16-color-schemes*
*minischeme*
*minicyan*
Base16 colorschemes ~
This module comes with several pre-built color schemes. All of them are a
|MiniBase16| theme created with faster version of the following Lua code: >lua
require('mini.base16').setup({ palette = palette, use_cterm = true })
<
Activate them as regular |colorscheme| (for example, `:colorscheme minischeme`).
## minischeme ~
Blue and yellow main colors with high contrast and saturation palette.
Palettes are: >lua
-- For dark 'background':
MiniBase16.mini_palette('#112641', '#e2e98f', 75)
-- For light 'background':
MiniBase16.mini_palette('#e2e5ca', '#002a83', 75)
<
## minicyan ~
Cyan and grey main colors with moderate contrast and saturation palette.
Palettes are: >lua
-- For dark 'background':
MiniBase16.mini_palette('#0A2A2A', '#D0D0D0', 50)
-- For light 'background':
MiniBase16.mini_palette('#C0D2D2', '#262626', 80)
<
------------------------------------------------------------------------------
*MiniBase16.setup()*
`MiniBase16.setup`({config})
Module setup
Setup is done by applying base16 palette to enable colorscheme. Highlight
groups make an extended set from original
[base16-vim](https://github.com/chriskempson/base16-vim/) plugin. It is a
good idea to have `config.palette` respect the original [styling
principles](https://github.com/chriskempson/base16/blob/master/styling.md).
By default only 'gui highlighting' (see |highlight-gui| and
|termguicolors|) is supported. To support 'cterm highlighting' (see
|highlight-cterm|) supply `config.use_cterm` argument in one of the formats:
- `true` to auto-generate from `palette` (as closest colors).
- Table with similar structure to `palette` but having terminal colors
(integers from 0 to 255) instead of hex strings.
Parameters ~
{config} `(table)` Module config table. See |MiniBase16.config|.
Usage ~
>lua
require('mini.base16').setup({}) -- replace {} with your config table
-- needs `palette` field present
<
------------------------------------------------------------------------------
*MiniBase16.config*
`MiniBase16.config`
Module config
Default values:
>lua
MiniBase16.config = {
-- Table with names from `base00` to `base0F` and values being strings of
-- HEX colors with format "#RRGGBB". NOTE: this should be explicitly
-- supplied in `setup()`.
palette = nil,
-- Whether to support cterm colors. Can be boolean, `nil` (same as
-- `false`), or table with cterm colors. See `setup()` documentation for
-- more information.
use_cterm = nil,
-- Plugin integrations. Use `default = false` to disable all integrations.
-- Also can be set per plugin (see |MiniBase16.config|).
plugins = { default = true },
}
<
# Options ~
## Plugin integrations ~
`config.plugins` defines for which supported plugins highlight groups will
be created. Limiting number of integrations slightly decreases startup time.
It is a table with boolean (`true`/`false`) values which are applied as follows:
- If plugin name (as listed in |mini.base16|) has entry, it is used.
- Otherwise `config.plugins.default` is used.
Example which will load only "mini.nvim" integration: >lua
require('mini.base16').setup({
palette = require('mini.base16').mini_palette('#112641', '#e2e98f', 75),
plugins = {
default = false,
['echasnovski/mini.nvim'] = true,
}
})
<
------------------------------------------------------------------------------
*MiniBase16.mini_palette()*
`MiniBase16.mini_palette`({background}, {foreground}, {accent_chroma})
Create 'mini' palette
Create base16 palette based on the HEX (string '#RRGGBB') colors of main
background and foreground with optional setting of accent chroma (see
details).
# Algorithm design ~
- Main operating color space is
[CIELCh(uv)](https://en.wikipedia.org/wiki/CIELUV#Cylindrical_representation_(CIELCh))
which is a cylindrical representation of a perceptually uniform CIELUV
color space. It defines color by three values: lightness L (values from 0
to 100), chroma (positive values), and hue (circular values from 0 to 360
degrees). Useful converting tool: https://www.easyrgb.com/en/convert.php
- There are four important lightness values: background, foreground, focus
(around the middle of background and foreground, leaning towards
foreground), and edge (extreme lightness closest to foreground).
- First four colors have the same chroma and hue as `background` but
lightness progresses from background towards focus.
- Second four colors have the same chroma and hue as `foreground` but
lightness progresses from foreground towards edge in such a way that
'base05' color is main foreground color.
- The rest eight colors are accent colors which are created in pairs
- Each pair has same hue from set of hues 'most different' to
background and foreground hues (if respective chorma is positive).
- All colors have the same chroma equal to `accent_chroma` (if not
provided, chroma of foreground is used, as they will appear next
to each other). Note: this means that in case of low foreground
chroma, it is a good idea to set `accent_chroma` manually.
Values from 30 (low chorma) to 80 (high chroma) are common.
- Within pair there is base lightness (equal to foreground
lightness) and alternative (equal to focus lightness). Base
lightness goes to colors which will be used more frequently in
code: base08 (variables), base0B (strings), base0D (functions),
base0E (keywords).
How exactly accent colors are mapped to base16 palette is a result of
trial and error. One rule of thumb was: colors within one hue pair should
be more often seen next to each other. This is because it is easier to
distinguish them and seems to be more visually appealing. That is why
`base0D` and `base0F` have same hues because they usually represent
functions and delimiter (brackets included).
Parameters ~
{background} `(string)` Background HEX color (formatted as `#RRGGBB`).
{foreground} `(string)` Foreground HEX color (formatted as `#RRGGBB`).
{accent_chroma} `(number)` Optional positive number (usually between 0
and 100). Default: chroma of foreground color.
Return ~
`(table)` Table with base16 palette.
Usage ~
>lua
local p = require('mini.base16').mini_palette('#112641', '#e2e98f', 75)
require('mini.base16').setup({ palette = p })
<
------------------------------------------------------------------------------
*MiniBase16.rgb_palette_to_cterm_palette()*
`MiniBase16.rgb_palette_to_cterm_palette`({palette})
Converts palette with RGB colors to terminal colors
Useful for caching `use_cterm` variable to increase speed.
Parameters ~
{palette} `(table)` Table with base16 palette (same as in
`MiniBase16.config.palette`).
Return ~
`(table)` Table with base16 palette using |highlight-cterm|.
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -0,0 +1,356 @@
*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|).
------------------------------------------------------------------------------
*MiniBasics.setup()*
`MiniBasics.setup`({config})
Module setup
Parameters ~
{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.config*
`MiniBasics.config`
Module config
Default values:
>lua
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,
}
<
*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.toggle_diagnostic()*
`MiniBasics.toggle_diagnostic`()
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.
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -0,0 +1,644 @@
*mini.bracketed* Go forward/backward with square brackets
*MiniBracketed*
MIT License Copyright (c) 2023 Evgeni Chasnovski
==============================================================================
Features:
- Configurable Lua functions to go forward/backward to a certain target.
Each function can be customized with:
- Direction. One of "forward", "backward", "first" (forward starting
from first one), "last" (backward starting from last one).
- Number of times to go.
- Whether to wrap on edges (going forward on last one goes to first).
- Some other target specific options.
- Mappings using square brackets. They are created using configurable
target suffix and can be selectively disabled.
Each mapping supports |[count]|. Mappings are created in Normal mode; for
targets which move cursor in current buffer also Visual and
Operator-pending (with dot-repeat) modes are supported.
Using `lower-suffix` and `upper-suffix` (lower and upper case suffix) for
a single target the following mappings are created:
- `[` + `upper-suffix` : go first.
- `[` + `lower-suffix` : go backward.
- `]` + `lower-suffix` : go forward.
- `]` + `upper-suffix` : go last.
- Supported targets (for more information see help for corresponding Lua
function):
`Target` `Mappings` `Lua function`
Buffer.......................... `[B` `[b` `]b` `]B` .... |MiniBracketed.buffer()|
Comment block................... `[C` `[c` `]c` `]C` .... |MiniBracketed.comment()|
Conflict marker................. `[X` `[x` `]x` `]X` .... |MiniBracketed.conflict()|
Diagnostic...................... `[D` `[d` `]d` `]D` .... |MiniBracketed.diagnostic()|
File on disk.................... `[F` `[f` `]f` `]F` .... |MiniBracketed.file()|
Indent change................... `[I` `[i` `]i` `]I` .... |MiniBracketed.indent()|
Jump from |jumplist|
inside current buffer........... `[J` `[j` `]j` `]J` .... |MiniBracketed.jump()|
Location from |location-list|..... `[L` `[l` `]l` `]L` .... |MiniBracketed.location()|
Old files....................... `[O` `[o` `]o` `]O` .... |MiniBracketed.oldfile()|
Quickfix entry from |Quickfix|.... `[Q` `[q` `]q` `]Q` .... |MiniBracketed.quickfix()|
Tree-sitter node and parents.... `[T` `[t` `]t` `]T` .... |MiniBracketed.treesitter()|
Undo states from specially
tracked linear history.......... `[U` `[u` `]u` `]U` .... |MiniBracketed.undo()|
Window in current tab........... `[W` `[w` `]w` `]W` .... |MiniBracketed.window()|
Yank selection replacing
latest put region................`[Y` `[y` `]y` `]Y` .... |MiniBracketed.yank()|
Notes:
- The `undo` target remaps |u| and |<C-R>| keys to register undo state
after undo and redo respectively. If this conflicts with your setup,
either disable `undo` target or make your remaps after calling
|MiniBracketed.setup()|. To use `undo` target, remap your undo/redo keys
to call |MiniBracketed.register_undo_state()| after the action.
# Setup ~
This module needs a setup with `require('mini.bracketed').setup({})` (replace
`{}` with your `config` table). It will create global Lua table `MiniBracketed`
which you can use for scripting or manually (with `:lua MiniBracketed.*`).
See |MiniBracketed.config| for available config settings.
You can override runtime config settings (like target options) locally
to buffer inside `vim.b.minibracketed_config` which should have same structure
as `MiniBracketed.config`. See |mini.nvim-buffer-local-config| for more details.
# Comparisons ~
- 'tpope/vim-unimpaired':
- Supports buffer, conflict, file, location, and quickfix targets mostly
via built-in commands (like |:bprevious|, etc.) without configuration.
- Supports files from argument list and tags. This module does not.
- Doesn't support most other this module's targets (comment, indent, ...).
- 'mini.indentscope':
- Target |MiniBracketed.indent()| target can go to "first" and "last"
indent change. It also can go not only to line with smaller indent,
but also bigger or different one.
- Mappings from 'mini.indentscope' have more flexibility in computation of
indent scope, like how to treat empty lines near border or whether to
compute indent at cursor.
# Disabling ~
To disable, set `vim.g.minibracketed_disable` (globally) or
`vim.b.minibracketed_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.
------------------------------------------------------------------------------
*MiniBracketed.setup()*
`MiniBracketed.setup`({config})
Module setup
Parameters ~
{config} `(table|nil)` Module config table. See |MiniBracketed.config|.
Usage ~
>lua
require('mini.bracketed').setup() -- use default config
-- OR
require('mini.bracketed').setup({}) -- replace {} with your config table
<
------------------------------------------------------------------------------
*MiniBracketed.config*
`MiniBracketed.config`
Module config
Default values:
>lua
MiniBracketed.config = {
-- First-level elements are tables describing behavior of a target:
--
-- - <suffix> - single character suffix. Used after `[` / `]` in mappings.
-- For example, with `b` creates `[B`, `[b`, `]b`, `]B` mappings.
-- Supply empty string `''` to not create mappings.
--
-- - <options> - table overriding target options.
--
-- See `:h MiniBracketed.config` for more info.
buffer = { suffix = 'b', options = {} },
comment = { suffix = 'c', options = {} },
conflict = { suffix = 'x', options = {} },
diagnostic = { suffix = 'd', options = {} },
file = { suffix = 'f', options = {} },
indent = { suffix = 'i', options = {} },
jump = { suffix = 'j', options = {} },
location = { suffix = 'l', options = {} },
oldfile = { suffix = 'o', options = {} },
quickfix = { suffix = 'q', options = {} },
treesitter = { suffix = 't', options = {} },
undo = { suffix = 'u', options = {} },
window = { suffix = 'w', options = {} },
yank = { suffix = 'y', options = {} },
}
<
Options ~
Each entry configures target with the same name and can have data configuring
mapping suffix and target options.
Example of configuration: >lua
require('mini.bracketed').setup({
-- Map [N, [n, ]n, ]N for conflict marker like in 'tpope/vim-unimpaired'
conflict = { suffix = 'n' },
-- Make diagnostic advance only by errors
diagnostic = { options = { severity = vim.diagnostic.severity.ERROR } },
-- Disable creation of mappings for `indent` target (for example,
-- in favor of ones from |mini.indentscope|)
indent = { suffix = '' },
-- Disable mappings for `window` target in favor of custom ones
window = { suffix = '' },
})
-- Create custom `window` mappings
local map = vim.keymap.set
map('n', '<Leader>wH', "<Cmd>lua MiniBracketed.window('first')<CR>")
map('n', '<Leader>wh', "<Cmd>lua MiniBracketed.window('backward')<CR>")
map('n', '<Leader>wl', "<Cmd>lua MiniBracketed.window('forward')<CR>")
map('n', '<Leader>wL', "<Cmd>lua MiniBracketed.window('last')<CR>")
<
## Suffix ~
The `suffix` key is used to create target mappings.
Supply empty string to disable mapping creation for that particular target.
To create a completely different mapping (like with |<Leader>|) use target
function manually.
Using `lower-suffix` and `upper-suffix` (lower and upper case suffix) for
a single target the following mappings are created:
- `[` + `upper-suffix` : go first.
- `[` + `lower-suffix` : go backward.
- `]` + `lower-suffix` : go forward.
- `]` + `upper-suffix` : go last.
When supplied with a non-letter, only forward/backward mappings are created.
## Options ~
The `options` key is directly forwarded as `opts` to corresponding Lua function.
------------------------------------------------------------------------------
*MiniBracketed.buffer()*
`MiniBracketed.buffer`({direction}, {opts})
Listed buffer
Go to next/previous listed buffer. Order by their number (see |bufnr()|).
Direction "forward" increases number, "backward" - decreases.
Parameters ~
{direction} `(string)` One of "first", "backward", "forward", "last".
{opts} `(table|nil)` Options. A table with fields:
- <n_times> `(number)` - Number of times to advance. Default: |v:count1|.
- <wrap> `(boolean)` - Whether to wrap around edges. Default: `true`.
------------------------------------------------------------------------------
*MiniBracketed.comment()*
`MiniBracketed.comment`({direction}, {opts})
Comment block
Go to next/previous comment block. Only linewise comments using
'commentsring' are recognized.
Direction "forward" increases line number, "backward" - decreases.
Parameters ~
{direction} `(string)` One of "first", "backward", "forward", "last".
{opts} `(table|nil)` Options. A table with fields:
- <n_times> `(number)` - Number of times to advance. Default: |v:count1|.
- <wrap> `(boolean)` - Whether to wrap around edges. Default: `true`.
- <add_to_jumplist> (`boolean`) - Whether to add current position to jumplist.
Default: `false`.
- <block_side> `(string)` - which side of comment block to use. One of
"near" (default; use nearest side), "start" (use first line), "end"
(use last line), "both" (use both first and last lines).
------------------------------------------------------------------------------
*MiniBracketed.conflict()*
`MiniBracketed.conflict`({direction}, {opts})
Git conflict marker
Go to next/previous lines containing Git conflict marker. That is, if it
starts with "<<<<<<< ", ">>>>>>> ", or is "=======".
Direction "forward" increases line number, "backward" - decreases.
Notes:
- Using this target in Operator-pending mode allows the following approach
at resolving merge conflicts:
- Place cursor on `=======` line.
- Execute one of these: `d]x[xdd` (choose upper part) or
`d[x]xdd` (choose lower part).
Parameters ~
{direction} `(string)` One of "first", "backward", "forward", "last".
{opts} `(table|nil)` Options. A table with fields:
- <n_times> `(number)` - Number of times to advance. Default: |v:count1|.
- <wrap> `(boolean)` - Whether to wrap around edges. Default: `true`.
- <add_to_jumplist> (`boolean`) - Whether to add current position to jumplist.
Default: `false`.
------------------------------------------------------------------------------
*MiniBracketed.diagnostic()*
`MiniBracketed.diagnostic`({direction}, {opts})
Diagnostic
Go to next/previous diagnostic. This is mostly similar to
|vim.diagnostic.goto_next()| and |vim.diagnostic.goto_prev()| for
current buffer which supports |[count]| and functionality to go to
first/last diagnostic entry.
Direction "forward" increases line number, "backward" - decreases.
Notes:
- Using `severity` option, this target can be used in mappings like "go to
next/previous error" (), etc. Using code similar to this: >lua
local severity_error = vim.diagnostic.severity.ERROR
-- Use these inside custom mappings
MiniBracketed.diagnostic('forward', { severity = severity_error })
MiniBracketed.diagnostic('backward', { severity = severity_error })
<
Parameters ~
{direction} `(string)` One of "first", "backward", "forward", "last".
{opts} `(table|nil)` Options. A table with fields:
- <n_times> `(number)` - Number of times to advance. Default: |v:count1|.
- <wrap> `(boolean)` - Whether to wrap around edges. Default: `true`.
- <float> `(boolean|table)` - control floating window after movement.
For available values see |vim.diagnostic.goto_next()|.
- <severity> `(string|table)` - which severity to use.
For available values see |diagnostic-severity|.
------------------------------------------------------------------------------
*MiniBracketed.file()*
`MiniBracketed.file`({direction}, {opts})
File on disk
Go to next/previous file on disk alphabetically. Files are taken from
directory of file in current buffer (or current working directory if buffer
doesn't contain a readable file). Only first-level files are used, i.e. it
doesn't go inside subdirectories.
Direction "forward" goes forward alphabetically, "backward" - backward.
Parameters ~
{direction} `(string)` One of "first", "backward", "forward", "last".
{opts} `(table|nil)` Options. A table with fields:
- <n_times> `(number)` - Number of times to advance. Default: |v:count1|.
- <wrap> `(boolean)` - Whether to wrap around edges. Default: `true`.
------------------------------------------------------------------------------
*MiniBracketed.indent()*
`MiniBracketed.indent`({direction}, {opts})
Indent change
Go to next/previous line with different indent (see |indent()|).
Can be used to go to lines with smaller, bigger, or different indent.
Notes:
- Directions "first" and "last" work differently from most other targets
for performance reasons. They are essentially "backward" and "forward"
with very big `n_times` option.
- For similar reasons, `wrap` is not supported.
- Blank line inherit indent from near non-blank line in direction of movement.
Direction "forward" increases line number, "backward" - decreases.
Parameters ~
{direction} `(string)` One of "first", "backward", "forward", "last".
{opts} `(table|nil)` Options. A table with fields:
- <n_times> `(number)` - Number of times to advance. Default: |v:count1|.
- <add_to_jumplist> (`boolean`) - Whether to add current position to jumplist.
Default: `false`.
- <change_type> `(string)` - which type of indent change to use.
One of "less" (default; smaller indent), "more" (bigger indent),
"diff" (different indent).
------------------------------------------------------------------------------
*MiniBracketed.jump()*
`MiniBracketed.jump`({direction}, {opts})
Jump inside current buffer
Go to next/previous jump from |jumplist| which is inside current buffer.
Notes:
- There are no Visual mode mappings due to implementation problems.
Direction "forward" increases jump number, "backward" - decreases.
Parameters ~
{direction} `(string)` One of "first", "backward", "forward", "last".
{opts} `(table|nil)` Options. A table with fields:
- <n_times> `(number)` - Number of times to advance. Default: |v:count1|.
- <wrap> `(boolean)` - Whether to wrap around edges. Default: `true`.
------------------------------------------------------------------------------
*MiniBracketed.location()*
`MiniBracketed.location`({direction}, {opts})
Location from location list
Go to next/previous location from |location-list|. This is similar to
|:lfirst|, |:lprevious|, |:lnext|, and |:llast| but with support of
wrapping around edges and |[count]| for "first"/"last" direction.
Direction "forward" increases location number, "backward" - decreases.
Parameters ~
{direction} `(string)` One of "first", "backward", "forward", "last".
{opts} `(table|nil)` Options. A table with fields:
- <n_times> `(number)` - Number of times to advance. Default: |v:count1|.
- <wrap> `(boolean)` - Whether to wrap around edges. Default: `true`.
------------------------------------------------------------------------------
*MiniBracketed.oldfile()*
`MiniBracketed.oldfile`({direction}, {opts})
Old files from previous and current sessions
Go to older/newer readable file either from previous session (see |v:oldfiles|)
or the current one (updated automatically after |MiniBracketed.setup()| call).
Direction "forward" goes to more recent files, "backward" - to older.
Notes:
- In current session it tracks only normal buffers (see |'buftype'|) for
some readable file.
- No new file is tracked when advancing this target. Only after buffer
change is done not through this target (like with |MiniBracketed.buffer()|),
it updates recency of last advanced and new buffers.
Parameters ~
{direction} `(string)` One of "first", "backward", "forward", "last".
{opts} `(table|nil)` Options. A table with fields:
- <n_times> `(number)` - Number of times to advance. Default: |v:count1|.
- <wrap> `(boolean)` - Whether to wrap around edges. Default: `true`.
------------------------------------------------------------------------------
*MiniBracketed.quickfix()*
`MiniBracketed.quickfix`({direction}, {opts})
Quickfix from quickfix list
Go to next/previous entry from |quickfix| list. This is similar to
|:cfirst|, |:cprevious|, |:cnext|, and |:clast| but with support of
wrapping around edges and |[count]| for "first"/"last" direction.
Direction "forward" increases location number, "backward" - decreases.
Parameters ~
{direction} `(string)` One of "first", "backward", "forward", "last".
{opts} `(table|nil)` Options. A table with fields:
- <n_times> `(number)` - Number of times to advance. Default: |v:count1|.
- <wrap> `(boolean)` - Whether to wrap around edges. Default: `true`.
------------------------------------------------------------------------------
*MiniBracketed.treesitter()*
`MiniBracketed.treesitter`({direction}, {opts})
Tree-sitter node
Go to end/start of current tree-sitter node and its parents (except root).
Notes:
- Requires |get_node_at_pos()| from |lua-treesitter| (present in Neovim=0.8)
or |vim.treesitter.get_node()| (present in Neovim>=0.9) along with loaded
tree-sitter parser in current buffer.
- Directions "first" and "last" work differently from most other targets
for performance reasons. They are essentially "backward" and "forward"
with very big `n_times` option.
- For similar reasons, `wrap` is not supported.
Direction "forward" moves cursor forward to node's end, "backward" - backward
to node's start.
Parameters ~
{direction} `(string)` One of "first", "backward", "forward", "last".
{opts} `(table|nil)` Options. A table with fields:
- <n_times> `(number)` - Number of times to advance. Default: |v:count1|.
- <add_to_jumplist> (`boolean`) - Whether to add current position to jumplist.
Default: `false`.
------------------------------------------------------------------------------
*MiniBracketed.undo()*
`MiniBracketed.undo`({direction}, {opts})
Undo along a tracked linear history
In a nutshell:
- Keys |u| and |<C-R>| (although remapped) can be used as usual, but every
their execution new state is recorded in this module's linear undo history.
- Advancing this target goes along linear undo history revealing undo states
**in order they actually appeared**.
- One big difference with built-in methods is that tracked linear history
can repeat undo states (not consecutively, though).
Neovim's default way of managing undo history is through branches (see
|undo-branches|). Basically it means that if you undo several changes and then
make new ones, it creates new undo branch while usually (see |'undolevels'|)
saving previous buffer states in another branch. While there are commands
to navigate by time of undo state creation (like |:earlier| and |:later|),
there is no intuitive way to cycle through them. Existing |g-| and |g+|
cycle through undo states **based on their creation time**, which often
gets confusing really guickly in extensively edited buffer.
This `undo()` target provides a way to cycle through linear undo history
**in order states actually appeared**. It does so by registering any new undo
states plus every time |MiniBracketed.register_undo_state()| is called. To have
more "out of the box" experience, |u| and |<C-R>| are remapped to call it after
they perform their undo/redo.
Example ~
To show more clearly the difference between advancing this target and using
built-in functionality, here is an example:
- Create undo history in a new buffer (|:new|):
- Enter `one two three` text.
- Delete first word with `daw` and undo the change with `u`.
- Delete second word with `daw` and undo the change with `u`.
- Delete third word with `daw` and undo the change with `u`.
- Now try one of the following (each one after performing previous steps in
separate new buffer):
- Press `u`. It goes back to empty buffer. Press `<C-R>` twice and it
goes to the latest change (`one two`). No way to get to other states
(like `two three` or `one three`) with these two keys.
- Press `g-`. It goes to an empty buffer. Press `g+` 4 times. It cycles
through all available undo states **in order they were created**.
- Finally, press `[u`. It goes back to `one two` - state which was
**previously visited** by the user. Another `[u` restores `one two three`.
Use `]U` to go to latest visited undo state.
Parameters ~
{direction} `(string)` One of "first", "backward", "forward", "last".
{opts} `(table|nil)` Options. A table with fields:
- <n_times> `(number)` - Number of times to advance. Default: |v:count1|.
- <wrap> `(boolean)` - Whether to wrap around edges. Default: `true`.
------------------------------------------------------------------------------
*MiniBracketed.register_undo_state()*
`MiniBracketed.register_undo_state`()
Register state for undo target
Use this function to add current undo state to this module's linear undo
history. It is used in |MiniBracketed.setup()| to remap |u| and |<C-R>| keys to add
their new state to linear undo history.
------------------------------------------------------------------------------
*MiniBracketed.window()*
`MiniBracketed.window`({direction}, {opts})
Normal window
Go to next/previous normal window. Order by their number (see |winnr()|).
Direction "forward" increases window number, "backward" - decreases.
Only normal (non-floating) windows are used.
Parameters ~
{direction} `(string)` One of "first", "backward", "forward", "last".
{opts} `(table|nil)` Options. A table with fields:
- <n_times> `(number)` - Number of times to advance. Default: |v:count1|.
- <wrap> `(boolean)` - Whether to wrap around edges. Default: `true`.
------------------------------------------------------------------------------
*MiniBracketed.yank()*
`MiniBracketed.yank`({direction}, {opts})
Replace "latest put region" with yank history entry
After |MiniBracketed.setup()| is called, on every yank/delete/change operation
(technically, every trigger of |TextYankPost| event) the object of operation
is added to yank history. Advancing this target will replace the region of
latest put operation with entry from yank history.
By default works best if called **right after** text paste (like with |p| or |P|).
To better detect "latest put region", use |MiniBracketed.register_put_region()|
as described later.
Direction "forward" goes to newer yank history entry, "backward" - to older.
Example ~
- Type `one two three`.
- Yank each word with `yiw`.
- Create new line and press `p`. This should paste `three`.
- Type `[y`. This should replace latest `three` with `two`.
Latest put region ~
"Latest put region" is (in order of decreasing priority):
- The one from latest advance of this target.
- The one registered by user with |MiniBracketed.register_put_region()|.
- The one taken from |`[| and |`]| marks.
For users there are these approaches to manage which region will be used:
- Do nothing. In this case region between `[` / `]` marks will always be used
for first `yank` advance.
Although doable, this has several drawbacks: it will use latest yanked or
changed region or the entire buffer if marks are not set.
If remember to advance this target only after recent put operation, this
should work as expected.
- Remap common put operations to use |MiniBracketed.register_put_region()|.
After that, only regions from mapped put operations will be used for first
advance. Example of custom mappings (note use of |:map-expression|): >lua
local put_keys = { 'p', 'P' }
for _, lhs in ipairs(put_keys) do
local rhs = 'v:lua.MiniBracketed.register_put_region("' .. lhs .. '")'
vim.keymap.set({ 'n', 'x' }, lhs, rhs, { expr = true })
end
<
Parameters ~
{direction} `(string)` One of "first", "backward", "forward", "last".
{opts} `(table|nil)` Options. A table with fields:
- <n_times> `(number)` - Number of times to advance. Default: |v:count1|.
- <wrap> `(boolean)` - Whether to wrap around edges. Default: `true`.
- <operators> `(table)` - array of operator names ("c", "d", or "y") for
which yank entry should be used to advance. For example, use `{ "y" }`
to advance only by entries actually resulted from yank operation with |y|.
Default: `{ 'c', 'd', 'y' }`.
------------------------------------------------------------------------------
*MiniBracketed.register_put_region()*
`MiniBracketed.register_put_region`({put_key})
Register "latest put region"
This function should be called after put register becomes relevant
(|v:register| is appropriately set) but before put operation takes place
(|`[| and |`]| marks become relevant).
Designed to be used in a user-facing expression mapping (see |:map-expression|).
For mapping examples see |MiniBracketed.yank()|.
Parameters ~
{put_key} `(string)` Put keys to be remapped.
Return ~
`(string)` Returns `put_key` for a better usage inside expression mappings.
------------------------------------------------------------------------------
*MiniBracketed.advance()*
`MiniBracketed.advance`({iterator}, {direction}, {opts})
Advance iterator
This is the main function which performs any forward/backward/first/last
advance in this module. Its basic idea is to take iterator (object containing
information about current state and how to go to next/previous one) and go
in certain direction until needed/allowed.
Notes:
- Directions "first" and "last" are convenience wrappers for "forward" and
"backward" with pre-setting initial state to `start_edge` and `end_edge`.
- Iterators `next()` and `prev()` methods should be able to handle `nil` as input.
- This function only returns new state and doesn't modify `iterator.state`.
Parameters ~
{iterator} `(table)` Table:
- Methods:
- <next> - given state, return state in forward direction (no wrap).
- <prev> - given state, return state in backward direction (no wrap).
- Fields:
- <state> - object describing current state.
- <start_edge> (optional) - object with `forward(start_edge)` describing
first state. If `nil`, can't wrap forward or use direction "first".
- <end_edge> (optional) - object with `backward(end_edge)` describing
last state. If `nil`, can't wrap backward or use direction "last".
{direction} `(string)` Direction. One of "first", "backward", "forward", "last".
{opts} `(table|nil)` Options with the following keys:
- <n_times> `(number)` - number of times to go in input direction.
Default: `v:count1`.
- <wrap> `(boolean)` - whether to wrap around edges when `next()` or
`prev()` return `nil`. Default: `true`.
Return ~
`(any)` Result state. If `nil`, could not reach any valid result state.
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -0,0 +1,129 @@
*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.
------------------------------------------------------------------------------
*MiniBufremove.setup()*
`MiniBufremove.setup`({config})
Module setup
Parameters ~
{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.config*
`MiniBufremove.config`
Module config
Default values:
>lua
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,
}
<
------------------------------------------------------------------------------
*MiniBufremove.delete()*
`MiniBufremove.delete`({buf_id}, {force})
Delete buffer `buf_id` with |:bdelete| after unshowing it
Parameters ~
{buf_id} `(number|nil)` Buffer identifier (see |bufnr()|) to use.
Default: 0 for current.
{force} `(boolean|nil)` Whether to ignore unsaved changes (using `!` version of
command). If `false`, calling with unsaved changes will prompt confirm dialog.
Default: `false`.
Return ~
`(boolean|nil)` Whether operation was successful. If `nil`, no operation was done.
------------------------------------------------------------------------------
*MiniBufremove.wipeout()*
`MiniBufremove.wipeout`({buf_id}, {force})
Wipeout buffer `buf_id` with |:bwipeout| after unshowing it
Parameters ~
{buf_id} `(number|nil)` Buffer identifier (see |bufnr()|) to use.
Default: 0 for current.
{force} `(boolean|nil)` Whether to ignore unsaved changes (using `!` version of
command). If `false`, calling with unsaved changes will prompt confirm dialog.
Default: `false`.
Return ~
`(boolean|nil)` Whether operation was successful. If `nil`, no operation was done.
------------------------------------------------------------------------------
*MiniBufremove.unshow()*
`MiniBufremove.unshow`({buf_id})
Stop showing buffer `buf_id` in all windows
Parameters ~
{buf_id} `(number|nil)` Buffer identifier (see |bufnr()|) to use.
Default: 0 for current.
Return ~
`(boolean|nil)` Whether operation was successful. If `nil`, no operation was done.
------------------------------------------------------------------------------
*MiniBufremove.unshow_in_window()*
`MiniBufremove.unshow_in_window`({win_id})
Stop showing current buffer of window `win_id`
Notes:
- If `win_id` represents |cmdline-window|, this function will close it.
Parameters ~
{win_id} `(number|nil)` Window identifier (see |win_getid()|) to use.
Default: 0 for current.
Return ~
`(boolean|nil)` Whether operation was successful. If `nil`, no operation was done.
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -0,0 +1,755 @@
*mini.clue* Show next key clues
*MiniClue*
MIT License Copyright (c) 2023 Evgeni Chasnovski
==============================================================================
Features:
- Implement custom key query process to reach target key combination:
- Starts after customizable opt-in triggers (mode + keys).
- Each key press narrows down set of possible targets.
Pressing `<BS>` removes previous user entry.
Pressing `<Esc>` or `<C-c>` leads to an early stop.
Doesn't depend on 'timeoutlen' and has basic support for 'langmap'.
- Ends when there is at most one target left or user pressed `<CR>`.
Results into emulating pressing all query keys plus possible postkeys.
- Show window (after configurable delay) with clues. It lists available
next keys along with their descriptions (auto generated from descriptions
present keymaps and user-supplied clues; preferring the former).
- Configurable "postkeys" for key combinations - keys which will be emulated
after combination is reached during key query process.
- Provide customizable sets of clues for common built-in keys/concepts:
- `g` key.
- `z` key.
- Window commands.
- Built-in completion.
- Marks.
- Registers.
- Lua functions to disable/enable triggers globally or per buffer.
For more details see:
- |MiniClue-key-query-process|.
- |MiniClue-examples|.
- |MiniClue.config|.
- |MiniClue.gen_clues|.
Notes:
- Works on all supported versions but using Neovim>=0.9 is recommended.
- There is no functionality to create mappings while defining clues.
This is done to clearly separate these two different actions.
The best suggested practice is to manually create mappings with
descriptions (`desc` field in options), as they will be automatically
used inside clue window.
- Triggers are implemented as special buffer-local mappings. This leads to
several caveats:
- They will override same regular buffer-local mappings and have
precedence over global one.
Example: having set `<C-w>` as Normal mode trigger means that
there should not be another `<C-w>` mapping.
- They need to be the latest created buffer-local mappings or they will
not function properly. Most common indicator of this is that some
mapping starts to work only after clue window is shown.
Example: `g` is set as Normal mode trigger, but `gcc` from |mini.comment|
doesn't work right away. This is probably because there are some
other buffer-local mappings starting with `g` which were created after
mapping for `g` trigger. Most common places for this are in LSP server's
`on_attach` or during tree-sitter start in buffer.
To check if trigger is the most recent buffer-local mapping, execute
`:<mode-char>map <trigger-keys>` (like `:nmap g` for previous example).
Mapping for trigger should be the first listed.
This module makes the best effort to work out of the box and cover
most common cases, but it is not foolproof. The solution here is to
ensure that triggers are created after making all buffer-local mappings:
run either |MiniClue.setup()| or |MiniClue.ensure_buf_triggers()|.
- Descriptions from existing mappings take precedence over user-supplied
clues. This is to ensure that information shown in clue window is as
relevant as possible. To add/customize description of an already existing
mapping, use |MiniClue.set_mapping_desc()|.
- Due to technical difficulties, there is no foolproof support for
Operator-pending mode triggers (like `a`/`i` from |mini.ai|):
- Doesn't work as part of a command in "temporary Normal mode" (like
after |i_CTRL-O|) due to implementation difficulties.
- Can have unexpected behavior with custom operators.
- Has (mostly solved) issues with macros:
- All triggers are disabled during macro recording due to technical
reasons.
- The `@` and `Q` keys are specially mapped inside |MiniClue.setup()|
to temporarily disable triggers.
# Setup ~
This module needs a setup with `require('mini.clue').setup({})` (replace
`{}` with your `config` table). It will create global Lua table `MiniClue`
which you can use for scripting or manually (with `:lua MiniClue.*`).
Config table **needs to have triggers configured**, none is set up by default.
See |MiniClue.config| for available config settings.
You can override runtime config settings (like clues or window options)
locally to a buffer inside `vim.b.miniclue_config` which should have same
structure as `MiniClue.config`. See |mini.nvim-buffer-local-config| for
more details.
# Comparisons ~
- 'folke/which-key.nvim':
- Both have the same main goal: show available next keys along with
their customizable descriptions.
- Has different UI and content layout.
- Allows creating mappings inside its configuration, while this module
doesn't have this by design (to clearly separate two different tasks).
- Doesn't allow creating submodes, while this module does (via `postkeys`).
- 'anuvyklack/hydra.nvim':
- Both allow creating submodes: state which starts at certain key
combination; treats some keys differently; ends after `<Esc>`.
- Doesn't show information about available next keys (outside of
submodes), while that is this module's main goal.
# Highlight groups ~
* `MiniClueBorder` - window border.
* `MiniClueDescGroup` - group description in clue window.
* `MiniClueDescSingle` - single target description in clue window.
* `MiniClueNextKey` - next key label in clue window.
* `MiniClueNextKeyWithPostkeys` - next key label with postkeys in clue window.
* `MiniClueSeparator` - separator in clue window.
* `MiniClueTitle` - window title.
To change any highlight group, modify it directly with |:highlight|.
# Disabling ~
To disable creating triggers, set `vim.g.miniclue_disable` (globally) or
`vim.b.miniclue_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.
------------------------------------------------------------------------------
*MiniClue-key-query-process*
# Key query process ~
## General info ~
This module implements custom key query process imitating a usual built-in
mechanism of user pressing keys in order to execute a mapping. General idea
is the same: narrow down key combinations until the target is reached.
Main goals of its existence are:
- Allow reaching certain mappings be independent of 'timeoutlen'. That is,
there is no fixed timeout after which currently typed keys are executed.
- Enable automated showing of next key clues after user-supplied delay
(also independent of 'timeoutlen').
- Allow emulating configurable key presses after certain key combination is
reached. This granular control allows creating so called "submodes".
See more at |MiniClue-examples-submodes|.
This process is primarily designed for nested `<Leader>` mappings in Normal
mode but works in all other main modes: Visual, Insert, Operator-pending
(with caveats; no foolproof guarantees), Command-line, Terminal.
## Lifecycle ~
- Key query process starts when user types a trigger: certain keys in certain
mode. Those keys are put into key query as a single user input. All possible
mode key combinations are filtered to ones starting with the trigger keys.
Note: trigger is implemented as a regular mapping, so if it has at least
two keys, they should be pressed within 'timeoutlen' milliseconds.
- Wait (indefinitely) for user to press a key. Advance depending on the key:
- Special key:
- If `<Esc>` or `<C-c>`, stop the process without any action.
- If `<CR>`, stop the process and execute current key query, meaning
emulate (with |nvim_feedkeys()|) user pressing those keys.
- If `<BS>`, remove previous user input from the query. If query becomes
empty, stop the process without any action.
- If a key for scrolling clue window (`scroll_down` / `scroll_up`
in `config.window`; `<C-d>` / `<C-u>` by default), scroll clue window
and wait for the next user key.
Note: if clue window is not shown, treated as a not special key.
- Not special key. Add key to the query while filtering all available
key combinations to start with the current key query. Advance:
- If there is a single available key combination matching current
key query, execute it.
- If there is no key combinations starting with the current query,
execute it. This, for instance, allows a seamless execution of
operators in presence of a longer key combinations. Example: with
`g` as trigger in Normal mode and available mappings `gc` / `gcc`
(like from |mini.comment|), this allows typing `gcip` to comment
current paragraph, although there are no key combinations
starting with `gci`.
- Otherwise wait for the new user key press.
## Clue window ~
After initiating key query process and after each key press, a timer is
started to show a clue window: floating window with information about
available next keys along with their descriptions. Note: if window is
already shown, its content is updated right away.
Clues can have these types:
- "Terminal next key": when pressed, will lead to query execution.
- "Terminal next key with postkeys": when pressed, will lead to query
execution plus some configured postkeys.
- "Group next key": when pressed, will narrow down available key combinations
and wait for another key press. Note: can have configured description
(inside `config.clues`) or it will be auto generated based on the number of
available key combinations.
------------------------------------------------------------------------------
*MiniClue-examples*
# Full starter example ~
If not sure where to start, try this example with all provided clues from
this module plus all |<Leader>| mappings in Normal and Visual modes: >lua
local miniclue = require('mini.clue')
miniclue.setup({
triggers = {
-- Leader triggers
{ mode = 'n', keys = '<Leader>' },
{ mode = 'x', keys = '<Leader>' },
-- Built-in completion
{ mode = 'i', keys = '<C-x>' },
-- `g` key
{ mode = 'n', keys = 'g' },
{ mode = 'x', keys = 'g' },
-- Marks
{ mode = 'n', keys = "'" },
{ mode = 'n', keys = '`' },
{ mode = 'x', keys = "'" },
{ mode = 'x', keys = '`' },
-- Registers
{ mode = 'n', keys = '"' },
{ mode = 'x', keys = '"' },
{ mode = 'i', keys = '<C-r>' },
{ mode = 'c', keys = '<C-r>' },
-- Window commands
{ mode = 'n', keys = '<C-w>' },
-- `z` key
{ mode = 'n', keys = 'z' },
{ mode = 'x', keys = 'z' },
},
clues = {
-- Enhance this by adding descriptions for <Leader> mapping groups
miniclue.gen_clues.builtin_completion(),
miniclue.gen_clues.g(),
miniclue.gen_clues.marks(),
miniclue.gen_clues.registers(),
miniclue.gen_clues.windows(),
miniclue.gen_clues.z(),
},
})
<
# Leader clues ~
Assume there are these |<Leader>| mappings set up: >lua
-- Set `<Leader>` before making any mappings and configuring 'mini.clue'
vim.g.mapleader = ' '
local nmap_leader = function(suffix, rhs, desc)
vim.keymap.set('n', '<Leader>' .. suffix, rhs, { desc = desc })
end
local xmap_leader = function(suffix, rhs, desc)
vim.keymap.set('x', '<Leader>' .. suffix, rhs, { desc = desc })
end
nmap_leader('bd', '<Cmd>lua MiniBufremove.delete()<CR>', 'Delete')
nmap_leader('bw', '<Cmd>lua MiniBufremove.wipeout()<CR>', 'Wipeout')
nmap_leader('lf', '<Cmd>lua vim.lsp.buf.format()<CR>', 'Format')
xmap_leader('lf', '<Cmd>lua vim.lsp.buf.format()<CR>', 'Format')
nmap_leader('lr', '<Cmd>lua vim.lsp.buf.rename()<CR>', 'Rename')
nmap_leader('lR', '<Cmd>lua vim.lsp.buf.references()<CR>', 'References')
<
The following setup will enable |<Leader>| as trigger in Normal and Visual
modes and add descriptions to mapping groups: >lua
require('mini.clue').setup({
-- Register `<Leader>` as trigger
triggers = {
{ mode = 'n', keys = '<Leader>' },
{ mode = 'x', keys = '<Leader>' },
},
-- Add descriptions for mapping groups
clues = {
{ mode = 'n', keys = '<Leader>b', desc = '+Buffers' },
{ mode = 'n', keys = '<Leader>l', desc = '+LSP' },
},
})
<
# Clues without mappings ~
Clues can be shown not only for actually present mappings. This is helpful for
showing clues for built-in key combinations. Here is an example of clues for
a subset of built-in completion (see |MiniClue.gen_clues.builtin_completion()|
to generate clues for all available completion sources): >lua
require('mini.clue').setup({
-- Make `<C-x>` a trigger. Otherwise, key query process won't start.
triggers = {
{ mode = 'i', keys = '<C-x>' },
},
-- Register custom clues
clues = {
{ mode = 'i', keys = '<C-x><C-f>', desc = 'File names' },
{ mode = 'i', keys = '<C-x><C-l>', desc = 'Whole lines' },
{ mode = 'i', keys = '<C-x><C-o>', desc = 'Omni completion' },
{ mode = 'i', keys = '<C-x><C-s>', desc = 'Spelling suggestions' },
{ mode = 'i', keys = '<C-x><C-u>', desc = "With 'completefunc'" },
}
})
<
*MiniClue-examples-submodes*
# Submodes ~
Submode is a state initiated after pressing certain key combination ("prefix")
during which some keys are interpreted differently.
In this module submode can be implemented following these steps:
- Create mappings for each key inside submode. Left hand side of mappings
should consist from prefix followed by the key.
- Create clue for each key inside submode with `postkeys` value equal to
prefix. It would mean that after executing particular key combination from
this submode, pressing its prefix will be automatically emulated (leading
back to being inside submode).
- Register submode prefix (or some of its starting part) as trigger.
## Submode examples ~
- Submode for moving with |mini.move|:
- Press `<Leader>m` to start submode.
- Press any of `h`/`j`/`k`/`l` to move selection/line.
- Press `<Esc>` to stop submode.
The code: >lua
require('mini.move').setup({
mappings = {
left = '<Leader>mh',
right = '<Leader>ml',
down = '<Leader>mj',
up = '<Leader>mk',
line_left = '<Leader>mh',
line_right = '<Leader>ml',
line_down = '<Leader>mj',
line_up = '<Leader>mk',
},
})
require('mini.clue').setup({
triggers = {
{ mode = 'n', keys = '<Leader>m' },
{ mode = 'x', keys = '<Leader>m' },
},
clues = {
{ mode = 'n', keys = '<Leader>mh', postkeys = '<Leader>m' },
{ mode = 'n', keys = '<Leader>mj', postkeys = '<Leader>m' },
{ mode = 'n', keys = '<Leader>mk', postkeys = '<Leader>m' },
{ mode = 'n', keys = '<Leader>ml', postkeys = '<Leader>m' },
{ mode = 'x', keys = '<Leader>mh', postkeys = '<Leader>m' },
{ mode = 'x', keys = '<Leader>mj', postkeys = '<Leader>m' },
{ mode = 'x', keys = '<Leader>mk', postkeys = '<Leader>m' },
{ mode = 'x', keys = '<Leader>ml', postkeys = '<Leader>m' },
},
})
<
- Submode for iterating buffers and windows with |mini.bracketed|:
- Press `[` or `]` to start key query process for certain direction.
- Press `b` / `w` to iterate buffers/windows until reach target one.
- Press `<Esc>` to stop submode.
The code: >lua
require('mini.bracketed').setup()
require('mini.clue').setup({
triggers = {
{ mode = 'n', keys = ']' },
{ mode = 'n', keys = '[' },
},
clues = {
{ mode = 'n', keys = ']b', postkeys = ']' },
{ mode = 'n', keys = ']w', postkeys = ']' },
{ mode = 'n', keys = '[b', postkeys = '[' },
{ mode = 'n', keys = '[w', postkeys = '[' },
},
})
<
- Submode for window commands using |MiniClue.gen_clues.windows()|:
- Press `<C-w>` to start key query process.
- Press keys which move / change focus / resize windows.
- Press `<Esc>` to stop submode.
The code: >lua
local miniclue = require('mini.clue')
miniclue.setup({
triggers = {
{ mode = 'n', keys = '<C-w>' },
},
clues = {
miniclue.gen_clues.windows({
submode_move = true,
submode_navigate = true,
submode_resize = true,
})
},
})
<
# Window config ~
>lua
require('mini.clue').setup({
triggers = { { mode = 'n', keys = '<Leader>' } },
window = {
-- Show window immediately
delay = 0,
config = {
-- Compute window width automatically
width = 'auto',
-- Use double-line border
border = 'double',
},
},
})
<
------------------------------------------------------------------------------
*MiniClue.setup()*
`MiniClue.setup`({config})
Module setup
Parameters ~
{config} `(table|nil)` Module config table. See |MiniClue.config|.
Usage ~
>lua
require('mini.clue').setup({}) -- replace {} with your config table
-- needs `triggers` field present
<
------------------------------------------------------------------------------
*MiniClue.config*
`MiniClue.config`
Module config
Default values:
>lua
MiniClue.config = {
-- Array of extra clues to show
clues = {},
-- Array of opt-in triggers which start custom key query process.
-- **Needs to have something in order to show clues**.
triggers = {},
-- Clue window settings
window = {
-- Floating window config
config = {},
-- Delay before showing clue window
delay = 1000,
-- Keys to scroll inside the clue window
scroll_down = '<C-d>',
scroll_up = '<C-u>',
},
}
<
# General info ~
- To use |<Leader>| as part of the config (either as trigger or inside clues),
set it prior to running |MiniClue.setup()|.
- See |MiniClue-examples| for examples.
# Clues ~
`config.clues` is an array with extra information about key combinations.
Each element can be one of:
- Clue table.
- Array (possibly nested) of clue tables.
- Callable (function) returning either of the previous two.
A clue table is a table with the following fields:
- <mode> `(string)` - single character describing **single** mode short-name of
key combination as in `nvim_set_keymap()` ('n', 'x', 'i', 'o', 'c', etc.).
- <keys> `(string)` - key combination for which clue will be shown.
"Human-readable" key names as in |key-notation| (like "<Leader>", "<Space>",
"<Tab>", etc.) are allowed.
- <desc> `(string|nil)` - optional key combination description which will
be shown in clue window.
- <postkeys> `(string|nil)` - optional postkeys which will be executed
automatically after `keys`. Allows creation of submodes
(see |MiniClue-examples-submodes|).
Notes:
- Postkeys are literal simulation of keypresses with |nvim_feedkeys()|.
- Suggested approach to configuring clues is to create mappings with `desc`
field while supplying to `config.clues` only elements describing groups,
postkeys, and built-in mappings.
# Triggers ~
`config.triggers` is an array with information when |MiniClue-key-query-process|
should start. Each element is a trigger table with the fields <mode> and
<keys> which are treated the same as in clue table.
# Window ~
`config.window` defines behavior of clue window.
`config.window.delay` is a number of milliseconds after which clue window will
appear. Can be 0 to show immediately.
`config.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 all clues). It should have the same
structure as in |nvim_open_win()| with the following enhancements:
- <width> field can be equal to `"auto"` leading to window width being
computed automatically based on its content. Default is fixed width of 30.
- <row> and <col> can be equal to `"auto"` in which case they will be
computed to "stick" to set anchor ("SE" by default; see |nvim_open_win()|).
This allows changing corner in which window is shown: >lua
-- Pick one anchor
local anchor = 'NW' -- top-left
local anchor = 'NE' -- top-right
local anchor = 'SW' -- bottom-left
local anchor = 'SE' -- bottom-right
require('mini.clue').setup({
window = {
config = { anchor = anchor, row = 'auto', col = 'auto' },
},
})
<
`config.window.scroll_down` / `config.window.scroll_up` are strings defining
keys which will scroll clue window down / up which is useful in case not
all clues fit in current window height. Set to empty string `''` to disable
either of them.
------------------------------------------------------------------------------
*MiniClue.enable_all_triggers()*
`MiniClue.enable_all_triggers`()
Enable triggers in all listed buffers
------------------------------------------------------------------------------
*MiniClue.enable_buf_triggers()*
`MiniClue.enable_buf_triggers`({buf_id})
Enable triggers in buffer
Parameters ~
{buf_id} `(number|nil)` Buffer identifier. Default: current buffer.
------------------------------------------------------------------------------
*MiniClue.disable_all_triggers()*
`MiniClue.disable_all_triggers`()
Disable triggers in all buffers
------------------------------------------------------------------------------
*MiniClue.disable_buf_triggers()*
`MiniClue.disable_buf_triggers`({buf_id})
Disable triggers in buffer
Parameters ~
{buf_id} `(number|nil)` Buffer identifier. Default: current buffer.
------------------------------------------------------------------------------
*MiniClue.ensure_all_triggers()*
`MiniClue.ensure_all_triggers`()
Ensure all triggers are valid
------------------------------------------------------------------------------
*MiniClue.ensure_buf_triggers()*
`MiniClue.ensure_buf_triggers`({buf_id})
Ensure buffer triggers are valid
Parameters ~
{buf_id} `(number|nil)` Buffer identifier. Default: current buffer.
------------------------------------------------------------------------------
*MiniClue.set_mapping_desc()*
`MiniClue.set_mapping_desc`({mode}, {lhs}, {desc})
Update description of an existing mapping
Notes:
- Uses buffer-local mapping in case there are both global and buffer-local
mappings with same mode and LHS. Similar to |maparg()|.
Parameters ~
{mode} `(string)` Mapping mode (as in `maparg()`).
{lhs} `(string)` Mapping left hand side (as `name` in `maparg()`).
{desc} `(string)` New description to set.
------------------------------------------------------------------------------
*MiniClue.gen_clues*
`MiniClue.gen_clues`
Generate pre-configured clues
This is a table with function elements. Call to actually get array of clues.
------------------------------------------------------------------------------
*MiniClue.gen_clues.builtin_completion()*
`MiniClue.gen_clues.builtin_completion`()
Generate clues for built-in completion
Contains clues for the following triggers: >lua
{ mode = 'i', keys = '<C-x>' }
<
Return ~
`(table)` Array of clues.
------------------------------------------------------------------------------
*MiniClue.gen_clues.g()*
`MiniClue.gen_clues.g`()
Generate clues for `g` key
Contains clues for the following triggers: >lua
{ mode = 'n', keys = 'g' }
{ mode = 'x', keys = 'g' }
<
Return ~
`(table)` Array of clues.
------------------------------------------------------------------------------
*MiniClue.gen_clues.marks()*
`MiniClue.gen_clues.marks`()
Generate clues for marks
Contains clues for the following triggers: >lua
{ mode = 'n', keys = "'" }
{ mode = 'n', keys = "g'" }
{ mode = 'n', keys = '`' }
{ mode = 'n', keys = 'g`' }
{ mode = 'x', keys = "'" }
{ mode = 'x', keys = "g'" }
{ mode = 'x', keys = '`' }
{ mode = 'x', keys = 'g`' }
<
Note: if you use "g" as trigger (like to enable |MiniClue.gen_clues.g()|),
don't add "g'" and "g`" as triggers: they already will be taken into account.
Return ~
`(table)` Array of clues.
See also ~
|mark-motions|
------------------------------------------------------------------------------
*MiniClue.gen_clues.registers()*
`MiniClue.gen_clues.registers`({opts})
Generate clues for registers
Contains clues for the following triggers: >lua
{ mode = 'n', keys = '"' }
{ mode = 'x', keys = '"' }
{ mode = 'i', keys = '<C-r>' }
{ mode = 'c', keys = '<C-r>' }
<
Parameters ~
{opts} `(table|nil)` Options. Possible keys:
- <show_contents> `(boolean)` - whether to show contents of all possible
registers. If `false`, only description of special registers is shown.
Default: `false`.
Return ~
`(table)` Array of clues.
See also ~
|registers|
------------------------------------------------------------------------------
*MiniClue.gen_clues.windows()*
`MiniClue.gen_clues.windows`({opts})
Generate clues for window commands
Contains clues for the following triggers: >lua
{ mode = 'n', keys = '<C-w>' }
<
Note: only non-duplicated commands are included. For full list see |CTRL-W|.
Parameters ~
{opts} `(table|nil)` Options. Possible keys:
- <submode_move> `(boolean)` - whether to make move (change layout)
commands a submode by using `postkeys` field. Default: `false`.
- <submode_navigate> `(boolean)` - whether to make navigation (change
focus) commands a submode by using `postkeys` field. Default: `false`.
- <submode_resize> `(boolean)` - whether to make resize (change size)
commands a submode by using `postkeys` field. Default: `false`.
Return ~
`(table)` Array of clues.
------------------------------------------------------------------------------
*MiniClue.gen_clues.z()*
`MiniClue.gen_clues.z`()
Generate clues for `z` key
Contains clues for the following triggers: >lua
{ mode = 'n', keys = 'z' }
{ mode = 'x', keys = 'z' }
<
Return ~
`(table)` Array of clues.
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -0,0 +1,836 @@
*mini.colors* Tweak and save any color scheme
*MiniColors*
MIT License Copyright (c) 2023 Evgeni Chasnovski
==============================================================================
Features:
- Create colorscheme object: either manually (|MiniColors.as_colorscheme()|)
or by querying present color schemes (including currently active one; see
|MiniColors.get_colorscheme()|).
- Infer data about color scheme and/or modify based on it:
- Add transparency by removing background color (requires transparency
in terminal emulator).
- Infer cterm attributes (|cterm-colors|) based on gui colors making it
compatible with 'notermguicolors'.
- Resolve highlight group links (|:highlight-link|).
- Compress by removing redundant highlight groups.
- Extract palette of used colors and/or infer terminal colors
(|terminal-config|) based on it.
- Modify colors to better fit your taste and/or goals (see more in
|MiniColors-colorscheme-methods|):
- Apply any function to color hex string.
- Update channels (like lightness, saturation, hue, temperature, red,
green, blue, etc.; see more in |MiniColors-channels|).
Use either own function or one of the implemented methods:
- Add value to channel or multiply it by coefficient. Like "add 10
to saturation of every color" or "multiply saturation by 2" to
make colors more saturated (less gray).
- Invert. Like "invert lightness" to convert between dark/light theme.
- Set to one or more values (picks closest to current one). Like
"set to one or two hues" to make mono- or dichromatic color scheme.
- Repel from certain source(s) with stronger effect for closer values.
Like "repel from hue 30" to remove red color from color scheme.
Repel hue (how much is removed) is configurable.
- Simulate color vision deficiency.
- Once color scheme is ready, either apply it to see effects right away or
write it into a Lua file as a fully functioning separate color scheme.
- Experiment interactively with a feedback (|MiniColors.interactive()|).
- Animate transition between color schemes either with |MiniColors.animate()|
or with |:Colorscheme| user command.
- Convert within supported color spaces (|MiniColors.convert()|):
- Hex string.
- 8-bit number (terminal colors).
- RGB.
- Oklab, Oklch, Okhsl (https://bottosson.github.io/posts/oklab/).
Notes:
- There is a collection of |MiniColors-recipes| with code snippets for some
common tasks.
- There is no goal to support as many color spaces as possible, only the
already present ones.
Tweak quick start ~
- Execute `:lua require('mini.colors').interactive()`.
- Experiment by writing calls to exposed color scheme methods and applying
them with `<M-a>`. For more information, see |MiniColors-colorscheme-methods|
and |MiniColors-recipes|.
- If you are happy with result, write color scheme with `<M-w>`. If not,
reset to initial color scheme with `<M-r>`.
- If only some highlight groups can be made better, adjust them manually
inside written color scheme file.
# Setup ~
This module doesn't need setup, but it can be done to improve usability.
Setup with `require('mini.colors').setup({})` (replace `{}` with your
`config` table). It will create global Lua table `MiniColors` which you can
use for scripting or manually (with `:lua MiniColors.*`).
See |MiniColors.config| for `config` structure and default values.
This module doesn't have runtime options, so using `vim.b.minicolors_config`
will have no effect here.
# Comparisons ~
- 'rktjmp/lush.nvim':
- Oriented towards tweaking separate highlight groups, while 'mini.colors'
is more designed to work with color scheme as a whole.
- Uses HSL and HSLuv color spaces, while 'mini.colors' uses Oklab, Oklch,
and Okhsl which have slightly better perceptual uniformity properties.
- Doesn't have functionality to infer and repair missing data in color
scheme (like cterm attributes, terminal colors, transparency, etc.),
while 'mini.colors' does.
- Doesn't implement animation of color scheme transition, while
'mini.colors' does.
- 'lifepillar/vim-colortemplate':
- Comparisons are similar to that of 'rktjmp/lush.nvim'.
- 'tjdevries/colorbuddy.nvim':
- Comparisons are similar to that of 'rktjmp/lush.nvim'.
------------------------------------------------------------------------------
*MiniColors-recipes*
Recipes for common tasks ~
All following code snippets assume to be executed inside interactive buffer
(|MiniColors.interactively()|). They are directly copy-pasteable.
To apply single method to current color scheme, use >vim
:lua MiniColors.get_colorscheme():<method goes here>:apply().
<
Recipes:
- Tweak lightness: >lua
-- Invert dark/light color scheme to be light/dark
chan_invert('lightness', { gamut_clip = 'cusp' })
-- Ensure constant contrast ratio
chan_set('lightness', 15, { filter = 'bg' })
chan_set('lightness', 85, { filter = 'fg' })
<
- Tweak saturation: >lua
-- Make background colors less saturated and foreground - more
chan_add('saturation', -20, { filter = 'bg' })
chan_add('saturation', 20, { filter = 'fg' })
-- Convert to grayscale
chan_set('saturation', 0)
<
- Tweak hue: >lua
-- Create monochromatic variant (this uses green color)
chan_set('hue', 135)
-- Create dichromatic variant (this uses Neovim-themed hues)
chan_set('hue', { 140, 245 })
<
- Tweak temperature: >lua
-- Invert temperature (make cold theme become warm and vice versa)
chan_invert('temperature')
-- Make background colors colder and foreground warmer
chan_add('temperature', -40, { filter = 'bg' })
chan_add('temperature', 40, { filter = 'fg' })
<
- Counter color vision deficiency (try combinations of these to see which
one works best for you):
- Improve text saturation contrast (usually the best starting approach): >lua
chan_set('saturation', { 10, 90 }, { filter = 'fg' })
<
- Remove certain hues from all colors (use 30 for red, 90 for yellow,
135 for green, 270 for blue): >lua
-- Repel red color
chan_repel('hue', 30, 45)
<
- Force equally spaced palette (remove ones with which you know you
have trouble): >lua
-- Might be a good choice for red-green color blindness
chan_set('hue', { 90, 180, 270})
-- Might be a good choice for blue-yellow color blindness
chan_set('hue', { 0, 90, 180 })
<
- Inverting temperature or pressure can sometimes improve readability: >lua
chan_invert('temperature')
chan_invert('pressure')
<
- If all hope is lost, hue random generation might help if you are lucky: >lua
chan_modify('hue', function() return math.random(0, 359) end)
<
- For color scheme creators use |MiniColors-colorscheme:simulate_cvd()| to
simulate various color vision deficiency types to see how color scheme
would look in the eyes of color blind person.
------------------------------------------------------------------------------
*MiniColors-color-spaces*
Color space is a way to quantitatively describe a color. In this module
color spaces are used both as source for |MiniColors-channels| and inputs
for |MiniColors.convert()|
List of supported color spaces (along with their id in parenthesis):
- 8-bit (`8-bit`) - integer between 16 and 255. Usually values 0-15 are also
supported, but they depend on terminal emulator theme which is not reliable.
See https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit .
- Hex (`hex`) - string of the form "#xxxxxx" where `x` is a hexadecimal number.
- RGB (`rgb`) - table with numeric fields `r` (red), `g` (green), `b` (blue).
Visible range is from 0 to 255.
- Oklab (`oklab`) - table with fields `l` (lightness; numeric in [0; 100]),
`a`, `b` (both are unbounded numeric; visible range is usually between
-30 to 30). Field `l` describes how light is color; `a` - how "green-red" it is;
`b` - how "blue-yellow" it is.
- Oklch (`oklch`) - table with fields `l` (same as in Oklab),
`c` (chroma; positive numeric, visible range usually lower than 32),
`h` (`nil` for grays or periodic numeric in [0, 360)). Field `c` describes how
colorful a color is; `h` is a value of "true color" on color circle/wheel.
NOTE: gray colors, being achromatic by nature, don't have hue.
- Okhsl (`okhsl`) - Oklch but with `c` replaced by `s` (saturation; numeric
in [0; 100]). Field `s` describes a percent of chroma relative to maximum
visible chroma for the particular lightness and hue combination. Note,
that mathematical model used to compute maximum visible chroma is
approximate which might lead to inaccuracies for highly saturated colors
with relatively low or high lightness.
Sources for Oklab/Oklch/Okhsl:
- https://bottosson.github.io/posts/oklab/ - initial derivation and
introduction of Oklab and Oklch.
- https://bottosson.github.io/misc/colorpicker - interactive color picker.
Great way for a hands-on introduction to concepts of lightness, chroma,
saturation, and hue.
*MiniColors-gamut-clip*
Gamut clip ~
In Neovim highlight group colors are usually specified by their red, green,
and blue values from 0 to 255 in the form of HEX string (see |gui-colors|).
Although plenty, these are not all possible colors.
When performing color manipulation using |MiniColors-colorscheme-methods|,
it is possible to end up with "impossible" color (which can't be directly
converted to HEX string). For example, inverting lightness of color "#fce094"
will lead to a color `{ l = 10, c = 10, h = 90 }` in Oklch space, i.e.
"dark yellow" which is impossible to show in HEX.
**Gamut clipping** is an action of converting color outside of visible gamut
(colors representable with HEX string) to be inside it while preserving
certain perceptual characteristics as much as possible.
Gamut clipping in this module is done inside Oklch color space. The goal is to
preserve hue as much as possible while manipulating lightness and/or chroma.
List of supported gamut clip methods (along with their id in parenthesis):
- Clip chroma (`'chroma'`) - reduce chroma while preserving lightness until
color is inside visible gamut. Default method.
- Clip lightness (`'lightness'`) - reduce lightness while preserving chroma
until color is inside visible gamut.
- Clip according to "cusp" (`'cusp'`) - reduce both lightness and chroma in
a compromise way depending on hue.
Cusp is a color with the highest chroma inside slice of visible gamut
with the same hue (hue leaf). It is called that way because the slice has
a roughly triangular shape with points at (0, 0) - (0, 100) - "cusp" in
(chroma, lightness) coordinates.
Gamut clipping using "cusp" as reference is done by changing color towards
(0, cusp_lightness) point (gray with lightness equal to that of a current
cusp) until color is inside visible gamut.
In short:
- Usually `'chroma'` is enough.
- If colors are too desaturated - try `'cusp'`.
- If still not colorful enough - try `'lightness'`.
Notes:
- Currently implemented formulas are approximate (by design; to reduce code
complexity) so there might be problems for highly saturated colors with
relatively low or high lightness.
------------------------------------------------------------------------------
*MiniColors-channels*
A color channel is a number describing one particular aspect of a color.
It is usually direct or modified coordinate of a color space. See
|MiniColors-color-spaces| for information on color spaces.
List of supported channels (along with their id in parenthesis):
- Lightness (`lightness`) - corrected `l` component of Oklch. Describes how
light is a color. Ranges from 0 (black dark) to 100 (white light).
- Chroma (`chroma`) - `c` component of Oklch. Describes how colorful is
a color in absolute units. Ranges from 0 (gray) to infinity (more like
around 30 in practice).
- Saturation (`saturation`) - `s` component of Okhsl. Describes how colorful
is color in relative units. Ranges from 0 (gray) to 100 (maximum saturation
for a given lightness-hue pair).
- Hue (`hue`) - `h` component of Oklch. Describes "true color value" (like
red/green/blue) as a number. It is a periodic value from 0 (included) to
360 (not included). Best perceived as a degree on a color circle/wheel.
Approximate values for common color names:
- 0 - pink.
- 30 - red.
- 60 - orange.
- 90 - yellow.
- 135 - green.
- 180 - cyan.
- 225 - light blue.
- 270 - blue.
- 315 - magenta/purple.
- Temperature (`temperature`) - circular distance from current hue to hue 270
angle (blue). Ranges from 0 (cool) to 180 (hot) anchored at hues 270 (blue)
and 90 (yellow). Similar to `b` channel but tries to preserve chroma.
- Pressure (`pressure`) - circular distance from current hue to hue 180.
Ranges from 0 (low; green-ish) to 180 (high; red-ish) anchored at hues
180 and 0. Similar to `a` channel but tries to preserve chroma.
Not widely used; added to have something similar to temperature.
- a (`a`) - `a` component of Oklab. Describes how "green-red" a color is.
Can have any value. Negative values are "green-ish", positive - "red-ish".
- b (`b`) - `b` component of Oklab. Describes how "blue-yellow" a color is.
Can have any value. Negative values are "blue-ish", positive - "yellow-ish".
- Red (`red`) - `r` component of RGB. Describes how much red a color has.
Ranges from 0 (no red) to 255 (full red).
- Green (`green`) - `g` component of RGB. Describes how much green a color has.
Ranges from 0 (no green) to 255 (full green).
- Blue (`blue`) - `b` component of RGB. Describes how much blue a color has.
Ranges from 0 (no blue) to 255 (full blue).
------------------------------------------------------------------------------
*MiniColors-colorscheme*
Colorscheme object is a central structure of this module. It contains all
data relevant to colors in fields and provides methods to modify it.
Create colorscheme object manually with |MiniColors.as_colorscheme()|: >lua
MiniColors.as_colorscheme({
name = 'my_cs',
groups = {
Normal = { fg = '#dddddd', bg = '#222222' },
SpellBad = { sp = '#dd2222', undercurl = true },
},
terminal = { [0] = '#222222', [1] = '#dd2222' },
})
<
Get any registered color scheme (including currently active) as colorscheme
object with |MiniColors.get_colorscheme()|: >lua
-- Get current color scheme
MiniColors.get_colorscheme()
-- Get registered color scheme by name
MiniColors.get_colorscheme('minischeme', { new_name = 'maxischeme' })
<
Class ~
{Colorscheme}
*MiniColors-colorscheme-fields*
Fields ~
{name} `(string|nil)` Name of the color scheme (as in |g:colors_name|).
{groups} `(table|nil)` Table with highlight groups data. Keys are group
names appropriate for `name` argument of |nvim_set_hl()|, values - tables
appropriate for its `val` argument. Note: gui colors are accepted only in
short form (`fg`, `bg`, `sp`).
{terminal} `(table|nil)` Table with terminal colors data (|terminal-config|).
Keys are numbers from 0 to 15, values - strings representing color (hex
string or plain color name; see |nvim_get_color_by_name()|).
*MiniColors-colorscheme-methods*
Notes about all methods:
- They never modify underlying colorscheme object instead returning deep
copy with modified fields.
- They accept `self` colorscheme object as first argument meaning they should be
called with `:` notation (like `cs:method()`).
Example calling methods: >lua
-- Get current color scheme, set hue of colors to 135, infer cterm
-- attributes and apply
local cs = MiniColors.get_colorscheme()
cs:chan_set('hue', 135):add_cterm_attributes():apply()
<
*MiniColors-colorscheme:add_cterm_attributes()*
Infer |cterm-colors| based on present |gui-colors|. It updates `ctermbg`/`ctermfg`
based on `fg`/`bg` by approximating in perceptually uniform distance in Oklab
space (|MiniColors-color-spaces|).
Parameters ~
{opts} `(table|nil)` Options. Possible fields:
- <force> `(boolean)` - Whether to replace already present cterm attributes
with inferred ones. Default: `true`.
*MiniColors-colorscheme:add_terminal_colors()*
Infer terminal colors (|terminal-config|) based on colorscheme palette
(see |MiniColors-colorscheme:get_palette()|). It updates `terminal` field
based on color scheme's palette by picking the most appropriate entry to
represent terminal color. Colors from 0 to 7 are attempted to be black,
red, green, yellow, blue, magenta, cyan, white. Colors from 8 to 15 are
the same as from 0 to 7.
Parameters ~
{opts} `(table|nil)` Options. Possible fields:
- <force> `(boolean)` - Whether to replace already present terminal colors
with inferred ones. Default: `true`.
- <palette_args> `(table)` - |MiniColors-colorscheme:get_palette()| arguments.
*MiniColors-colorscheme:add_transparency()*
Add transparency by removing background from a certain highlight groups.
Requires actual transparency from terminal emulator to experience visible
transparency.
Parameters ~
{opts} `(table|nil)` Options. Possible fields can be used to configure which
sets of highlight groups to update:
- <general> `(boolean)` - general groups (like `Normal`). Default: `true`.
- <float> `(boolean)` - built-in groups for floating windows. Default: `false`.
- <statuscolumn> `(boolean)` - groups related to 'statuscolumn' (signcolumn,
numbercolumn, foldcolumn). Also updates groups for all currently
defined signs. Default: `false`.
- <statusline> `(boolean)` - built-in groups for 'statusline'. Default: `false`.
- <tabline> `(boolean)` - built-in groups for 'tabline'. Default: `false`.
- <winbar> `(boolean)` - built-in groups for 'winbar'. Default: `false`.
*MiniColors-colorscheme:apply()*
Apply colorscheme:
- Set |g:colors_name| to a `name` field.
- Apply highlight groups in a `groups` field.
- Set terminal colors from a `terminal` field.
Parameters ~
{opts} `(table|nil)` Options. Possible fields:
- <clear> `(boolean)` - whether to execute |:hi-clear| first. Default: `true`.
*MiniColors-colorscheme:chan_add()*
Add value to a channel (see |MiniColors-channels|).
Parameters ~
{channel} `(string)` One of supported |MiniColors-channels|.
{value} `(number)` Number to add (can be negative).
{opts} `(table|nil)` Options. Possible fields:
- <filter> `(function|string)` - filter colors to update. Possible values:
- String representing target attributes. One of `'fg'`, `'bg'`, `'sp'`,
`'term'` (only terminal colors).
- Callable with signature as in |MiniColors-colorscheme:color_modify()|.
Default: `nil` to update all colors.
- <gamut_clip> `(string)` - gamut clipping method. One of `'chroma'`,
`'lightness'`, `'cusp'`. See |MiniColors-gamut-clip|. Default: `'chroma'`.
*MiniColors-colorscheme:chan_invert()*
Invert value in a channel (see |MiniColors-channels|).
Notes:
- Most Oklab/Oklch inversions are not exactly invertible: applying it twice
might lead to slightly different colors depending on gamut clip method
(|MiniColors-gamut-clip|) like smaller chroma with default `'chroma'` method.
Parameters ~
{channel} `(string)` One of supported |MiniColors-channels|.
{opts} `(table|nil)` Options. Possible fields:
- <filter> `(function|string)` - filter colors to update. Possible values:
- String representing target attributes. One of `'fg'`, `'bg'`, `'sp'`,
`'term'` (only terminal colors).
- Callable with signature as in |MiniColors-colorscheme:color_modify()|.
Default: `nil` to update all colors.
- <gamut_clip> `(string)` - gamut clipping method. One of `'chroma'`,
`'lightness'`, `'cusp'`. See |MiniColors-gamut-clip|. Default: `'chroma'`.
*MiniColors-colorscheme:chan_modify()*
Modify channel with a callable.
Parameters ~
{channel} `(string)` One of supported |MiniColors-channels|.
{f} `(function)` - callable which defines modification. Should take current
value of a channel and return a new one.
{opts} `(table|nil)` Options. Possible fields:
- <filter> `(function|string)` - filter colors to update. Possible values:
- String representing target attributes. One of `'fg'`, `'bg'`, `'sp'`,
`'term'` (only terminal colors).
- Callable with signature as in |MiniColors-colorscheme:color_modify()|.
Default: `nil` to update all colors.
- <gamut_clip> `(string)` - gamut clipping method. One of `'chroma'`,
`'lightness'`, `'cusp'`. See |MiniColors-gamut-clip|. Default: `'chroma'`.
*MiniColors-colorscheme:chan_multiply()*
Multiply value of a channel (see |MiniColors-channels|).
Parameters ~
{channel} `(string)` One of supported |MiniColors-channels|.
{coef} `(number)` Number to multiply with (can be negative).
{opts} `(table|nil)` Options. Possible fields:
- <filter> `(function|string)` - filter colors to update. Possible values:
- String representing target attributes. One of `'fg'`, `'bg'`, `'sp'`,
`'term'` (only terminal colors).
- Callable with signature as in |MiniColors-colorscheme:color_modify()|.
Default: `nil` to update all colors.
- <gamut_clip> `(string)` - gamut clipping method. One of `'chroma'`,
`'lightness'`, `'cusp'`. See |MiniColors-gamut-clip|. Default: `'chroma'`.
*MiniColors-colorscheme:chan_repel()*
Repel from certain sources.
Given an array of repel centers (`sources`) and repel degree (`coef`) add to
current channel value some amount ("nudge") with the following properties:
- Nudges from several sources are added together.
- Nudge is directly proportional to `coef`: bigger `coef` means bigger nudge.
- Nudge is inversely proportional to the distance between current value and
source: for positive `coef` bigger distance means smaller nudge, i.e.
repel effect weakens with distance.
- With positive `coef` nudges close to source are computed in a way to remove
whole `[source - coef; source + coef]` range.
- Negative `coef` results into attraction to source. Nudges in
`[source - coef; source + coef]` range are computed to completely collapse it
into `source`.
Examples: >lua
-- Repel hue from red color removing hue in range from 20 to 40
chan_repel('hue', 30, 10)
-- Attract hue to red color collapsing [20; 40] range into 30.
chan_repel('hue', 30, -10)
<
Parameters ~
{channel} `(string)` One of supported |MiniColors-channels|.
{sources} `(table|number)` Single or multiple source from which to repel.
{coef} `(number)` Repel degree (can be negative to attract).
{opts} `(table|nil)` Options. Possible fields:
- <filter> `(function|string)` - filter colors to update. Possible values:
- String representing target attributes. One of `'fg'`, `'bg'`, `'sp'`,
`'term'` (only terminal colors).
- Callable with signature as in |MiniColors-colorscheme:color_modify()|.
Default: `nil` to update all colors.
- <gamut_clip> `(string)` - gamut clipping method. One of `'chroma'`,
`'lightness'`, `'cusp'`. See |MiniColors-gamut-clip|. Default: `'chroma'`.
*MiniColors-colorscheme:chan_set()*
Set channel to certain value(s). This can be used to ensure that channel has
value(s) only within supplied set. If more than one is supplied, closest
element to current value is used.
Parameters ~
{channel} `(string)` One of supported |MiniColors-channels|.
{values} `(table|number)` Single or multiple values to set.
{opts} `(table|nil)` Options. Possible fields:
- <filter> `(function|string)` - filter colors to update. Possible values:
- String representing target attributes. One of `'fg'`, `'bg'`, `'sp'`,
`'term'` (only terminal colors).
- Callable with signature as in |MiniColors-colorscheme:color_modify()|.
Default: `nil` to update all colors.
- <gamut_clip> `(string)` - gamut clipping method. One of `'chroma'`,
`'lightness'`, `'cusp'`. See |MiniColors-gamut-clip|. Default: `'chroma'`.
*MiniColors-colorscheme:color_modify()*
Modify all colors with a callable. It should return new color value (hex
string or `nil` to remove attribute) base on the following input:
- Current color as hex string.
- Data about the color: a table with fields:
- <attr> - one of `'fg'`, `'bg'`, `'sp'`, and `'term'` for terminal color.
- <name> - name of color source. Either a name of highlight group or
string of the form `terminal_color_x` for terminal color (as in
|terminal-config|).
Example: >lua
-- Set to '#dd2222' all foreground colors for groups starting with "N"
color_modify(function(hex, data)
if data.attr == 'fg' and data.name:find('^N') then
return '#dd2222'
end
return hex
end)
<
Parameters ~
{f} `(function)` Callable returning new color value.
*MiniColors-colorscheme:compress()*
Remove redundant highlight groups. These are one of the two kinds:
- Having values identical to ones after |:hi-clear| (meaning they usually
don't add new information).
- Coming from a curated list of plugins with highlight groups usually not
worth keeping around. Current list of such plugins:
- 'nvim-tree/nvim-web-devicons'
- 'norcalli/nvim-colorizer.lua'
This method is useful to reduce size of color scheme before writing into
the file with |MiniColors-colorscheme:write()|.
Parameters ~
{opts} `(table|nil)` Options. Possible fields:
- <plugins> `(boolean)` - whether to remove highlight groups from a curated
list of plugins. Default: `true`.
*MiniColors-colorscheme:get_palette()*
Get commonly used colors. This basically counts number of all color
occurrences and filter out rare ones.
It is usually a good idea to apply both |MiniColors-colorscheme:compress()|
and |MiniColors-colorscheme:resolve_links()| before applying this.
Parameters ~
{opts} `(table|nil)` Options. Possible fields:
- <threshold> `(number)` - relative threshold for groups to keep. A group
is not included in output if it has less than this many occurrences
relative to a total number of colors. Default: 0.01.
*MiniColors-colorscheme:resolve_links()*
Resolve links (|:highlight-link|). This makes all highlight groups with `link`
attribute have data from a linked one.
Notes:
- Resolves nested links.
- If some group is linked to a group missing in current colorscheme object,
it is not resolved.
*MiniColors-colorscheme:simulate_cvd()*
Simulate color vision deficiency (CVD, color blindness). This is basically
a wrapper using |MiniColors.simulate_cvd()| as a part of
call to |MiniColors-colorscheme:color_modify()| method.
Parameters ~
{cvd_type} `(string)` One of `'protan'`, `'deutan'`, `'tritan'`, `'mono'`.
{severity} `(number|nil)` Severity of CVD, number between 0 and 1. Default: 1.
*MiniColors-colorscheme:write()*
Write color scheme to a file. It will be a Lua script readily usable as
a regular color scheme. Useful to both save results of color scheme tweaking
and making local snapshot of some other color scheme.
Sourcing this file on startup usually leads to a better performance that
sourcing initial color scheme, as it is essentially a conditioned
|:hi-clear| call followed by a series of |nvim_set_hl()| calls.
Default writing location is a "colors" directory of your Neovim config
directory (see |base-directories|). After writing, it should be available
for sourcing with |:colorscheme| or |:Colorscheme|.
Name of the file by default is taken from `name` field (`'mini_colors'` is
used if it is `nil`). If color scheme with this name already exists, it
appends prefix based on current time to make it unique.
Notes:
- If colors were updated, it is usually a good idea to infer cterm attributes
with |MiniColors-colorscheme:add_cterm_attributes()| prior to writing.
Parameters ~
{opts} `(table|nil)` Options. Possible fields:
- <compress> `(boolean)` - whether to call |MiniColors-colorscheme:compress()|
prior to writing. Default: `true`.
- <name> `(string|nil)` - basename of written file. Default: `nil` to infer
from `name` field.
- <directory> `(string)` - directory to where file should be saved.
Default: "colors" subdirectory of Neovim home config (`stdpath("config")`).
------------------------------------------------------------------------------
*MiniColors.setup()*
`MiniColors.setup`({config})
Module setup
*:Colorscheme*
Calling this function creates a `:Colorscheme` user command. It takes one or
more registered color scheme names and performs animated transition between
them (starting from currently active color scheme).
It uses |MiniColors.animte()| with default options.
Parameters ~
{config} `(table|nil)` Module config table. See |MiniColors.config|.
Usage ~
>lua
require('mini.colors').setup() -- use default config
-- OR
require('mini.colors').setup({}) -- replace {} with your config table
<
------------------------------------------------------------------------------
*MiniColors.config*
`MiniColors.config`
Module config
Default values:
>lua
MiniColors.config = {}
<
------------------------------------------------------------------------------
*MiniColors.as_colorscheme()*
`MiniColors.as_colorscheme`({x})
Create colorscheme object
Parameters ~
{x} `(table)` Table to be transformed into |MiniColors-colorscheme| object.
Return ~
`(table)` Copy of `x` transformed into a colorscheme object.
------------------------------------------------------------------------------
*MiniColors.get_colorscheme()*
`MiniColors.get_colorscheme`({name}, {opts})
Get colorscheme object from registered color scheme
Parameters ~
{name} `(string|nil)` Name of color scheme to use. If `nil` (default) creates
colorscheme object based on currently active data (|g:colors_name|,
highlight groups, terminal colors). If string, converts color scheme with
that name to a colorscheme object.
{opts} `(table|nil)` Options. Possible fields:
- <new_name> `(string|nil)` - new name of colorscheme object.
Return ~
`(table)` Colorscheme object |(MiniColors-colorscheme|).
------------------------------------------------------------------------------
*MiniColors.interactive()*
`MiniColors.interactive`({opts})
Start interactive experiments
Create a special buffer in which user can write plain Lua code to tweak
color scheme and apply to get visual feedback.
General principles ~
- Initial colorscheme object is fixed to interactive buffer on its creation.
- There are special buffer convenience mappings:
- Apply (source) current buffer content.
- Reset color scheme (make initial colorscheme the current one).
- Write to a file the result of applying current buffer content.
This sources current content and calls |MiniColors-colorscheme:write()|.
- Quit interactive buffer.
- User is expected to iteratively tweak color scheme by writing general Lua
code in interactive buffer and applying it using convenience mapping.
- Application of interactive buffer is essentially these steps:
- Expose `self` as initial colorscheme object on any application.
It is always the same for every application.
- Expose initial colorscheme methods as standalone functions. So instead
of writing `self = self:add_transparency()` user can only write
`add_transparency()`.
- Source buffer content as plain Lua code.
Example of interactive buffer content: >lua
chan_modify('hue', function() return math.random(0, 359) end)
simulate_cvd('protan')
add_cterm_attributes()
add_terminal_colors()
<
Parameters ~
{opts} `(table|nil)` Options. Possible fields:
- <colorscheme> `(table|nil)` - |MiniColors-colorscheme| object to be
used as initial colorscheme for executed code. By default uses current
color scheme.
- <mappings> `table` - buffer mappings for actions. Possible fields:
- <Apply> `(string)` - apply buffer code. Default: `'<M-a>'`.
- <Reset> `(string)` - apply initial color scheme as is. Default: `'<M-r>'`.
- <Quit> `(string)` - close interactive buffer. Default: `'<M-q>'`.
- <Write> `(string)` - write result of buffer code into a file.
Prompts for file name with |vim.ui.input()| and then
uses |MiniColors-colorscheme:write()| with other options being default.
Default: `'<M-w>'`.
------------------------------------------------------------------------------
*MiniColors.animate()*
`MiniColors.animate`({cs_array}, {opts})
Animate color scheme change
Start from currently active color scheme and loop through `cs_array`.
Powers |:Colorscheme| user command created in |MiniColors.setup()|.
Parameters ~
{cs_array} `(`(table)`)` Array of |MiniColors-colorscheme| objects.
{opts} `(table|nil)` Options. Possible fields:
- <transition_steps> `(number)` - number of intermediate steps to show
during transition between two color schemes. Bigger values result in
smoother visual feedback but require more computational power.
Default: 25.
- <transition_duration> `(number)` - number of milliseconds to spend
showing transition. Default: 1000.
- <show_duration> `(number)` - number of milliseconds to show intermediate
color schemes (all but last in `cs_array`). Default: 1000.
------------------------------------------------------------------------------
*MiniColors.convert()*
`MiniColors.convert`({x}, {to_space}, {opts})
Convert between color spaces
For a list of supported colors spaces see |MiniColors-color-spaces|.
Parameters ~
{x} `(table|string|number|nil)` Color to convert from. Its color space is
inferred automatically.
{to_space} `(string)` Id of allowed color space.
{opts} `(table|nil)` Options. Possible fields:
- <gamut_clip> `(string)` - method for |MiniColors-gamut-clip|.
Default: `'chroma'`.
Return ~
`(table|string|number|nil)` Color in space `to_space` or `nil` if input is `nil`.
------------------------------------------------------------------------------
*MiniColors.modify_channel()*
`MiniColors.modify_channel`({x}, {channel}, {f}, {opts})
Modify channel
Parameters ~
{x} `(table|string|number|nil)` Color which channel will be modified. Color
space is inferred automatically.
{channel} `(string)` One of supported |MiniColors-channels|.
{f} `(function)` Callable which defines modification. Should take current
value of a channel and return a new one.
{opts} `(table|nil)` Options. Possible fields:
- <gamut_clip> `(string)` - method for |MiniColors-gamut-clip|.
Default: `'chroma'`.
Return ~
`(string|nil)` Hex string of color with modified channel or `nil` if input is `nil`.
------------------------------------------------------------------------------
*MiniColors.simulate_cvd()*
`MiniColors.simulate_cvd`({x}, {cvd_type}, {severity})
Simulate color vision deficiency
Parameters ~
{x} `(table|string|number|nil)` Color to convert from. Its color space is
inferred automatically.
{cvd_type} `(string)` Type of CVD. One of `'protan'`, `'deutan'`,
or `'mono'` (equivalent to converting to graysacle).
{severity} `(number|nil)` Severity of CVD. A number between 0 and 1 (default).
Return ~
`(string|nil)` Hex string of simulated color or `nil` if input is `nil`.
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -0,0 +1,231 @@
*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.
------------------------------------------------------------------------------
*MiniComment.setup()*
`MiniComment.setup`({config})
Module setup
Parameters ~
{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.config*
`MiniComment.config`
Module config
Default values:
>lua
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,
},
}
<
# 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.operator()*
`MiniComment.operator`({mode})
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.
Parameters ~
{mode} `(string|nil)` Optional string with 'operatorfunc' mode (see |g@|).
Return ~
`(string|nil)` 'g@' if called without argument, '' otherwise (but after
performing action).
------------------------------------------------------------------------------
*MiniComment.toggle_lines()*
`MiniComment.toggle_lines`({line_start}, {line_end}, {opts})
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.
Parameters ~
{line_start} `(number)` Start line number (inclusive from 1 to number of lines).
{line_end} `(number)` End line number (inclusive from 1 to number of lines).
{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.textobject()*
`MiniComment.textobject`()
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.get_commentstring()*
`MiniComment.get_commentstring`({ref_position})
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.
Parameters ~
{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'.
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -0,0 +1,273 @@
*mini.completion* Completion and signature help
*MiniCompletion*
MIT License Copyright (c) 2021 Evgeni Chasnovski
==============================================================================
Key design ideas:
- Have an async (with customizable "debounce" delay) "two-stage chain
completion": first try to get completion items from LSP client (if set
up) and if no result, fallback to custom action.
- Managing completion is done as much with Neovim's built-in tools as
possible.
Features:
- Two-stage chain completion:
- First stage is an LSP completion implemented via
|MiniCompletion.completefunc_lsp()|. It should be set up as either
|completefunc| or |omnifunc|. It tries to get completion items from
LSP client (via 'textDocument/completion' request). Custom
preprocessing of response items is possible (with
`MiniCompletion.config.lsp_completion.process_items`), for example
with fuzzy matching. By default items which are not snippets and
directly start with completed word are kept and sorted according to
LSP specification. Supports `additionalTextEdits`, like auto-import
and others (see 'Notes').
- If first stage is not set up or resulted into no candidates, fallback
action is executed. The most tested actions are Neovim's built-in
insert completion (see |ins-completion|).
- Automatic display in floating window of completion item info (via
'completionItem/resolve' request) and signature help (with highlighting
of active parameter if LSP server provides such information). After
opening, window for signature help is fixed and is closed when there is
nothing to show, text is different or
when leaving Insert mode.
- Automatic actions are done after some configurable amount of delay. This
reduces computational load and allows fast typing (completion and
signature help) and item selection (item info)
- User can force two-stage completion via
|MiniCompletion.complete_twostage()| (by default is mapped to
`<C-Space>`) or fallback completion via
|MiniCompletion.complete_fallback()| (mapped to `<M-Space>`).
What it doesn't do:
- Snippet expansion.
- Many configurable sources.
- Automatic mapping of `<CR>`, `<Tab>`, etc., as those tend to have highly
variable user expectations. See 'Helpful key mappings' for suggestions.
# Setup ~
This module needs a setup with `require('mini.completion').setup({})`
(replace `{}` with your `config` table). It will create global Lua table
`MiniCompletion` which you can use for scripting or manually (with
`:lua MiniCompletion.*`).
See |MiniCompletion.config| for `config` structure and default values.
You can override runtime config settings locally to buffer inside
`vim.b.minicompletion_config` which should have same structure as
`MiniCompletion.config`. See |mini.nvim-buffer-local-config| for more details.
# Notes ~
- More appropriate, albeit slightly advanced, LSP completion setup is to set
it not on every `BufEnter` event (default), but on every attach of LSP
client. To do that:
- Use in initial config:
`lsp_completion = {source_func = 'omnifunc', auto_setup = false}`.
- In `on_attach()` of every LSP client set 'omnifunc' option to exactly
`v:lua.MiniCompletion.completefunc_lsp`.
- Uses `vim.lsp.protocol.CompletionItemKind` map in LSP step to show a readable
version of item's kind. Modify it directly to change what is displayed.
If you have |mini.icons| enabled, take a look at |MiniIcons.tweak_lsp_kind()|.
- If you have trouble using custom (overridden) |vim.ui.input| (like from
'stevearc/dressing.nvim'), make automated disable of 'mini.completion'
for input buffer. For example, currently for 'dressing.nvim' it can be
with `au FileType DressingInput lua vim.b.minicompletion_disable = true`.
- Support of `additionalTextEdits` tries to handle both types of servers:
- When `additionalTextEdits` are supplied in response to
'textDocument/completion' request (like currently in 'pyright').
- When `additionalTextEdits` are supplied in response to
'completionItem/resolve' request (like currently in
'typescript-language-server'). In this case to apply edits user needs
to trigger such request, i.e. select completion item and wait for
`MiniCompletion.config.delay.info` time plus server response time.
# Comparisons ~
- 'nvim-cmp':
- More complex design which allows multiple sources each in form of
separate plugin. `MiniCompletion` has two built in: LSP and fallback.
- Supports snippet expansion.
- Doesn't have customizable delays for basic actions.
- Doesn't allow fallback action.
- Doesn't provide signature help.
# Helpful mappings ~
To use `<Tab>` and `<S-Tab>` for navigation through completion list, make
these mappings: >lua
local imap_expr = function(lhs, rhs)
vim.keymap.set('i', lhs, rhs, { expr = true })
end
imap_expr('<Tab>', [[pumvisible() ? "\<C-n>" : "\<Tab>"]])
imap_expr('<S-Tab>', [[pumvisible() ? "\<C-p>" : "\<S-Tab>"]])
<
To get more consistent behavior of `<CR>`, you can use this template in
your 'init.lua' to make customized mapping: >lua
local keycode = vim.keycode or function(x)
return vim.api.nvim_replace_termcodes(x, true, true, true)
end
local keys = {
['cr'] = keycode('<CR>'),
['ctrl-y'] = keycode('<C-y>'),
['ctrl-y_cr'] = keycode('<C-y><CR>'),
}
_G.cr_action = function()
if vim.fn.pumvisible() ~= 0 then
-- If popup is visible, confirm selected item or add new line otherwise
local item_selected = vim.fn.complete_info()['selected'] ~= -1
return item_selected and keys['ctrl-y'] or keys['ctrl-y_cr']
else
-- If popup is not visible, use plain `<CR>`. You might want to customize
-- according to other plugins. For example, to use 'mini.pairs', replace
-- next line with `return require('mini.pairs').cr()`
return keys['cr']
end
end
vim.keymap.set('i', '<CR>', 'v:lua._G.cr_action()', { expr = true })
<
# Highlight groups ~
* `MiniCompletionActiveParameter` - signature active parameter.
By default displayed as plain underline.
To change any highlight group, modify it directly with |:highlight|.
# Disabling ~
To disable, set `vim.g.minicompletion_disable` (globally) or
`vim.b.minicompletion_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.
------------------------------------------------------------------------------
*MiniCompletion.setup()*
`MiniCompletion.setup`({config})
Module setup
Parameters ~
{config} `(table|nil)` Module config table. See |MiniCompletion.config|.
Usage ~
>lua
require('mini.completion').setup() -- use default config
-- OR
require('mini.completion').setup({}) -- replace {} with your config table
<
------------------------------------------------------------------------------
*MiniCompletion.config*
`MiniCompletion.config`
Module config
Default values:
>lua
MiniCompletion.config = {
-- Delay (debounce type, in ms) between certain Neovim event and action.
-- This can be used to (virtually) disable certain automatic actions by
-- setting very high delay time (like 10^7).
delay = { completion = 100, info = 100, signature = 50 },
-- Configuration for action windows:
-- - `height` and `width` are maximum dimensions.
-- - `border` defines border (as in `nvim_open_win()`).
window = {
info = { height = 25, width = 80, border = 'none' },
signature = { height = 25, width = 80, border = 'none' },
},
-- Way of how module does LSP completion
lsp_completion = {
-- `source_func` should be one of 'completefunc' or 'omnifunc'.
source_func = 'completefunc',
-- `auto_setup` should be boolean indicating if LSP completion is set up
-- on every `BufEnter` event.
auto_setup = true,
-- `process_items` should be a function which takes LSP
-- 'textDocument/completion' response items and word to complete. Its
-- output should be a table of the same nature as input items. The most
-- common use-cases are custom filtering and sorting. You can use
-- default `process_items` as `MiniCompletion.default_process_items()`.
process_items = --<function: filters out snippets; sorts by LSP specs>,
},
-- Fallback action. It will always be run in Insert mode. To use Neovim's
-- built-in completion (see `:h ins-completion`), supply its mapping as
-- string. Example: to use 'whole lines' completion, supply '<C-x><C-l>'.
fallback_action = --<function: like `<C-n>` completion>,
-- Module mappings. Use `''` (empty string) to disable one. Some of them
-- might conflict with system mappings.
mappings = {
force_twostep = '<C-Space>', -- Force two-step completion
force_fallback = '<A-Space>', -- Force fallback completion
},
-- Whether to set Vim's settings for better experience (modifies
-- `shortmess` and `completeopt`)
set_vim_settings = true,
}
<
------------------------------------------------------------------------------
*MiniCompletion.complete_twostage()*
`MiniCompletion.complete_twostage`({fallback}, {force})
Run two-stage completion
Parameters ~
{fallback} `(boolean|nil)` Whether to use fallback completion. Default: `true`.
{force} `(boolean|nil)` Whether to force update of completion popup.
Default: `true`.
------------------------------------------------------------------------------
*MiniCompletion.complete_fallback()*
`MiniCompletion.complete_fallback`()
Run fallback completion
------------------------------------------------------------------------------
*MiniCompletion.stop()*
`MiniCompletion.stop`({actions})
Stop actions
This stops currently active (because of module delay or LSP answer delay)
actions.
Designed to be used with |autocmd|. No need to use it directly, everything
is setup in |MiniCompletion.setup|.
Parameters ~
{actions} `(table|nil)` Array containing any of 'completion', 'info', or
'signature' string. Default: array containing all of them.
------------------------------------------------------------------------------
*MiniCompletion.completefunc_lsp()*
`MiniCompletion.completefunc_lsp`({findstart}, {base})
Module's |complete-function|
This is the main function which enables two-stage completion. It should be
set as one of |completefunc| or |omnifunc|.
No need to use it directly, everything is setup in |MiniCompletion.setup|.
------------------------------------------------------------------------------
*MiniCompletion.default_process_items()*
`MiniCompletion.default_process_items`({items}, {base})
Default `MiniCompletion.config.lsp_completion.process_items`
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -0,0 +1,107 @@
*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()')
<
------------------------------------------------------------------------------
*MiniCursorword.setup()*
`MiniCursorword.setup`({config})
Module setup
Parameters ~
{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.config*
`MiniCursorword.config`
Module config
Default values:
>lua
MiniCursorword.config = {
-- Delay (in ms) between when cursor moved and when highlighting appeared
delay = 100,
}
<
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -0,0 +1,578 @@
*mini.deps* Plugin manager
*MiniDeps*
MIT License Copyright (c) 2024 Evgeni Chasnovski
==============================================================================
Features:
- Manage plugins utilizing Git and built-in |packages| with these actions:
- Add plugin to current session, download if absent. See |MiniDeps.add()|.
- Update with/without confirm, with/without parallel download of new data.
See |MiniDeps.update()|.
- Delete unused plugins with/without confirm. See |MiniDeps.clean()|.
- Get / set / save / load snapshot. See `MiniDeps.snap_*()` functions.
All main actions are available both as Lua functions and user commands
(see |MiniDeps-commands|).
- Minimal yet flexible plugin |MiniDeps-plugin-specification|:
- Plugin source.
- Name of target plugin directory.
- Checkout target: branch, commit, tag, etc.
- Monitor branch to track updates without checking out.
- Dependencies to be set up prior to the target plugin.
- Hooks to call before/after plugin is created/changed.
- Helpers implementing two-stage startup: |MiniDeps.now()| and |MiniDeps.later()|.
See |MiniDeps-overview| for how to implement basic lazy loading with them.
What it doesn't do:
- Manage plugins which are developed without Git. The suggested approach is
to create a separate package (see |packages|).
- Provide ways to completely remove or update plugin's functionality in
current session. Although this is partially doable, it can not be done
in full (yet) because plugins can have untraceable side effects
(autocmmands, mappings, etc.).
The suggested approach is to restart Nvim.
Sources with more details:
- |MiniDeps-overview|
- |MiniDeps-plugin-specification|
- |MiniDeps-commands|
# Dependencies ~
For most of its functionality this plugin relies on `git` CLI tool.
See https://git-scm.com/ for more information about how to install it.
Actual knowledge of Git is not required but helpful.
# Setup ~
This module needs a setup with `require('mini.deps').setup({})` (replace
`{}` with your `config` table). It will create global Lua table `MiniDeps`
which you can use for scripting or manually (with `:lua MiniDeps.*`).
See |MiniDeps.config| for `config` structure and default values.
You can override runtime config settings locally to buffer inside
`vim.b.minideps_config` which should have same structure as
`MiniDeps.config`. See |mini.nvim-buffer-local-config| for more details.
# Comparisons ~
- 'folke/lazy.nvim':
- More feature-rich and complex.
- Uses table specification with dedicated functions to add plugins,
while this module uses direct function call approach
(calling |MiniDeps.add()| ensures that plugin is usable).
- Uses version tags by default, while this module is more designed towards
tracking branches. Using tags is possible too (see |MiniDeps-overview|).
- 'savq/paq-nvim':
- Overall less feature-rich than this module (by design).
- Uses array of plugin specifications inside `setup()` call to define which
plugins should be installed. Requires separate `:PaqInstall` call to
actually install them. This module ensures installation on first load.
- 'junegunn/vim-plug':
- Written in Vimscript, while this module is in Lua.
- Similar approach to defining and installing plugins as 'savq/paq-nvim'.
- Has basic lazy-loading built-in, while this module does not (by design).
# Highlight groups ~
Highlight groups are used inside confirmation buffers after
default |MiniDeps.update()| and |MiniDeps.clean()|.
* `MiniDepsChangeAdded` - added change (commit) during update.
* `MiniDepsChangeRemoved` - removed change (commit) during update.
* `MiniDepsHint` - various hints.
* `MiniDepsInfo` - various information.
* `MiniDepsMsgBreaking` - message for (conventional commit) breaking change.
* `MiniDepsPlaceholder` - placeholder when there is no valuable information.
* `MiniDepsTitle` - various titles.
* `MiniDepsTitleError` - title when plugin had errors during update.
* `MiniDepsTitleSame` - title when plugin has no changes to update.
* `MiniDepsTitleUpdate` - title when plugin has changes to update.
To change any highlight group, modify it directly with |:highlight|.
------------------------------------------------------------------------------
*MiniDeps-overview*
# Directory structure ~
This module uses built-in |packages| to make plugins usable in current session.
It works with "pack/deps" package inside `config.path.package` directory.
By default "opt" subdirectory is used to install optional plugins which are
loaded on demand with |MiniDeps.add()|.
Non-optional plugins in "start" subdirectory are supported but only if moved
there manually after initial install. Use it if you know what you are doing.
# Add plugin ~
Use |MiniDeps.add()| to add plugin to current session. Supply plugin's URL
source as a string or |MiniDeps-plugin-specification| in general. If plugin is
not present in "pack/deps" package, it will be created (a.k.a. installed)
before processing anything else.
The recommended way of adding a plugin is by calling |MiniDeps.add()| in the
|init.lua| file (make sure |MiniDeps.setup()| is called prior): >lua
local add = MiniDeps.add
-- Add to current session (install if absent)
add({
source = 'neovim/nvim-lspconfig',
-- Supply dependencies near target plugin
depends = { 'williamboman/mason.nvim' },
})
add({
source = 'nvim-treesitter/nvim-treesitter',
-- Use 'master' while monitoring updates in 'main'
checkout = 'master',
monitor = 'main',
-- Perform action after every checkout
hooks = { post_checkout = function() vim.cmd('TSUpdate') end },
})
-- Possible to immediately execute code which depends on the added plugin
require('nvim-treesitter.configs').setup({
ensure_installed = { 'lua', 'vimdoc' },
highlight = { enable = true },
})
<
NOTE:
- To increase performance, `add()` only ensures presence on disk and
nothing else. In particular, it doesn't ensure `opts.checkout` state.
Update or modify plugin state explicitly (see later sections).
# Lazy loading ~
Any lazy-loading is assumed to be done manually by calling |MiniDeps.add()|
at appropriate time. This module provides helpers implementing special safe
two-stage loading:
- |MiniDeps.now()| safely executes code immediately. Use it to load plugins
with UI necessary to make initial screen draw.
- |MiniDeps.later()| schedules code to be safely executed later, preserving
order. Use it (with caution) for everything else which doesn't need
precisely timed effect, as it will be executed some time soon on one of
the next event loops. >lua
local now, later = MiniDeps.now, MiniDeps.later
-- Safely execute immediately
now(function() vim.cmd('colorscheme randomhue') end)
now(function() require('mini.statusline').setup() end)
-- Safely execute later
later(function() require('mini.pick').setup() end)
<
# Update ~
To update plugins from current session with new data from their sources,
use |:DepsUpdate|. This will download updates (utilizing multiple cores) and
show confirmation buffer. Follow instructions at its top to finish an update.
NOTE: This updates plugins on disk which most likely won't affect current
session. Restart Nvim to have them properly loaded.
# Modify ~
To change plugin's specification (like set different `checkout`, etc.):
- Update corresponding |MiniDeps.add()| call.
- Run `:DepsUpdateOffline <plugin_name>`.
- Review changes and confirm.
- Restart Nvim.
NOTE: if `add()` prior used a single source string, make sure to convert
its argument to `{ source = '<previous_argument>', checkout = '<state>'}`
# Snapshots ~
Use |:DepsSnapSave| to save state of all plugins from current session into
a snapshot file (see `config.path.snapshot`).
Use |:DepsSnapLoad| to load snapshot. This will change (without confirmation)
state on disk. Plugins present in both snapshot file and current session
will be affected. Restart Nvim to see the effect.
NOTE: loading snapshot does not change plugin's specification defined inside
|MiniDeps.add()| call. This means that next update might change plugin's state.
To make it permanent, freeze plugin in target state manually.
# Freeze ~
Modify plugin's specification to have `checkout` pointing to a static
target: tag, state (commit hash), or 'HEAD' (to freeze in current state).
Frozen plugins will not receive updates. You can monitor any new changes from
its source by "subscribing" to `monitor` branch which will be shown inside
confirmation buffer after |:DepsUpdate|.
Example: use `checkout = 'v0.10.0'` to freeze plugin at tag "v0.10.0" while
monitoring new versions in the log from `monitor` (usually default) branch.
# Rollback ~
To roll back after an unfortunate update:
- Get identifier of latest working state:
- Use |:DepsShowLog| to see update log, look for plugin's name, and copy
identifier listed as "State before:".
- See previously saved snapshot file for plugin's name and copy
identifier next to it.
- Freeze plugin at that state while monitoring appropriate branch.
Revert to previous shape of |MiniDeps.add()| call to resume updating.
# Remove ~
- Make sure that target plugin is not registered in current session.
Usually it means removing corresponding |MiniDeps.add()| call.
- Run |:DepsClean|. This will show confirmation buffer with a list of plugins to
be deleted from disk. Follow instructions at its top to finish cleaning.
Alternatively, manually delete plugin's directory from "pack/deps" package.
------------------------------------------------------------------------------
*MiniDeps-plugin-specification*
# Plugin specification ~
Each plugin dependency is managed based on its specification (a.k.a. "spec").
See |MiniDeps-overview| for some examples.
Specification can be a single string which is inferred as:
- Plugin <name> if it doesn't contain "/".
- Plugin <source> otherwise.
Primarily, specification is a table with the following fields:
- <source> `(string|nil)` - field with URI of plugin source used during creation
or update. Can be anything allowed by `git clone`.
Default: `nil` to rely on source set up during install.
Notes:
- It is required for creating plugin, but can be omitted afterwards.
- As the most common case, URI of the format "user/repo" is transformed
into "https://github.com/user/repo".
- <name> `(string|nil)` - directory basename of where to put plugin source.
It is put in "pack/deps/opt" subdirectory of `config.path.package`.
Default: basename of <source> if it is present, otherwise should be
provided explicitly.
- <checkout> `(string|nil)` - checkout target used to set state during update.
Can be anything supported by `git checkout` - branch, commit, tag, etc.
Default: `nil` for default branch (usually "main" or "master").
- <monitor> `(string|nil)` - monitor branch used to track new changes from
different target than `checkout`. Should be a name of present Git branch.
Default: `nil` for default branch (usually "main" or "master").
- <depends> `(table|nil)` - array of plugin specifications (strings or tables)
to be added prior to the target.
Default: `nil` for no dependencies.
- <hooks> `(table|nil)` - table with callable hooks to call on certain events.
Possible hook names:
- <pre_install> - before creating plugin directory.
- <post_install> - after creating plugin directory.
- <pre_checkout> - before making change in existing plugin.
- <post_checkout> - after making change in existing plugin.
Each hook is executed with the following table as an argument:
- <path> (`string`) - absolute path to plugin's directory
(might not yet exist on disk).
- <source> (`string`) - resolved <source> from spec.
- <name> (`string`) - resolved <name> from spec.
Default: `nil` for no hooks.
------------------------------------------------------------------------------
*MiniDeps-commands*
# User commands ~
Note: Most commands have a Lua function alternative which they rely on.
Like |:DepsAdd| uses |MiniDeps.add()|, etc.
*:DepsAdd*
`:DepsAdd user/repo` makes plugin from https://github.com/user/repo available
in the current session (also creates it, if it is not present).
`:DepsAdd name` adds already installed plugin `name` to current session.
Accepts only single string compatible with |MiniDeps-plugin-specification|.
To add plugin in every session, put |MiniDeps.add()| in |init.lua|.
*:DepsUpdate*
`:DepsUpdate` synchronizes plugins with their session specifications and
updates them with new changes from sources. It shows confirmation buffer in
a separate |tabpage| with information about an upcoming update to review
and (selectively) apply. See |MiniDeps.update()| for more info.
`:DepsUpdate name` updates plugin `name`. Any number of names is allowed.
`:DepsUpdate!` and `:DepsUpdate! name` update without confirmation.
You can see what was done in the log file afterwards (|:DepsShowLog|).
*:DepsUpdateOffline*
`:DepsUpdateOffline` is same as |:DepsUpdate| but doesn't download new updates
from sources. Useful to only synchronize plugin specification in code and
on disk without unnecessary downloads.
*:DepsShowLog*
`:DepsShowLog` opens log file to review.
*:DepsClean*
`:DepsClean` deletes plugins from disk not added to current session. It shows
confirmation buffer in a separate |tabpage| with information about an upcoming
deletes to review and (selectively) apply. See |MiniDeps.clean()| for more info.
`:DepsClean!` deletes plugins without confirmation.
*:DepsSnapSave*
`:DepsSnapSave` creates snapshot file in default location (see |MiniDeps.config|).
`:DepsSnapSave path` creates snapshot file at `path`.
*:DepsSnapLoad*
`:DepsSnapLoad` loads snapshot file from default location (see |MiniDeps.config|).
`:DepsSnapLoad path` loads snapshot file at `path`.
------------------------------------------------------------------------------
*MiniDeps.setup()*
`MiniDeps.setup`({config})
Module setup
Calling this function creates user commands described in |MiniDeps-commands|.
Parameters ~
{config} `(table|nil)` Module config table. See |MiniDeps.config|.
Usage ~
>lua
require('mini.deps').setup() -- use default config
-- OR
require('mini.deps').setup({}) -- replace {} with your config table
<
------------------------------------------------------------------------------
*MiniDeps.config*
`MiniDeps.config`
Module config
Default values:
>lua
MiniDeps.config = {
-- Parameters of CLI jobs
job = {
-- Number of parallel threads to use. Default: 80% of all available.
n_threads = nil,
-- Timeout (in ms) for each job before force quit
timeout = 30000,
},
-- Paths describing where to store data
path = {
-- Directory for built-in package.
-- All plugins are actually stored in 'pack/deps' subdirectory.
package = vim.fn.stdpath('data') .. '/site',
-- Default file path for a snapshot
snapshot = vim.fn.stdpath('config') .. '/mini-deps-snap',
-- Log file
log = vim.fn.stdpath('log') .. '/mini-deps.log'
},
-- Whether to disable showing non-error feedback
silent = false,
}
<
# Job ~
`config.job` defines how CLI jobs are run.
`job.n_threads` is a maximum number of parallel jobs used when needed.
Default: 80% of all available.
`job.timeout` is a duration (in ms) from job start until it is forced to stop.
Default: 30000.
# Paths ~
`config.path` defines main paths used in this module.
`path.package` is a string with path inside which "pack/deps" package is stored
(see |MiniDeps-overview|).
Default: "site" subdirectory of "data" standard path (see |stdpath()|).
`path.snapshot` is a string with default path for snapshot.
See |:DepsSnapSave| and |:DepsSnapLoad|.
Default: "mini-deps-snap" file in "config" standard path (see |stdpath()|).
`path.log` is a string with path containing log of operations done by module.
In particular, it contains all changes done after making an update.
Default: "mini-deps.log" file in "log" standard path (see |stdpath()|).
# Silent ~
`config.silent` is a boolean controlling whether to suppress non-error feedback.
Default: `false`.
------------------------------------------------------------------------------
*MiniDeps.add()*
`MiniDeps.add`({spec}, {opts})
Add plugin to current session
- Process specification by expanding dependencies into single spec array.
- Ensure plugin is present on disk along with its dependencies by installing
(in parallel) absent ones:
- Execute `opts.hooks.pre_install`.
- Use `git clone` to clone plugin from its source URI into "pack/deps/opt".
- Set state according to `opts.checkout`.
- Execute `opts.hooks.post_install`.
- Register spec(s) in current session.
- Make sure plugin(s) can be used in current session (see |:packadd|).
- If not during startup and is needed, source all "after/plugin/" scripts.
Notes:
- Presence of plugin is checked by its name which is the same as the name
of its directory inside "pack/deps" package (see |MiniDeps-overview|).
- To increase performance, this function only ensures presence on disk and
nothing else. In particular, it doesn't ensure `opts.checkout` state.
Use |MiniDeps.update()| or |:DepsUpdateOffline| explicitly.
- Adding plugin several times updates its session specs.
Parameters ~
{spec} `(table|string)` Plugin specification. See |MiniDeps-plugin-specification|.
{opts} `(table|nil)` Options. Possible fields:
- <bang> `(boolean)` - whether to use `:packadd!` instead of plain |:packadd|.
------------------------------------------------------------------------------
*MiniDeps.update()*
`MiniDeps.update`({names}, {opts})
Update plugins
- Synchronize specs with state of plugins on disk (set `source`, etc.).
- Infer data before downloading updates.
- If not offline, download updates (in parallel).
- Infer data after downloading updates.
- If update is forced, apply all changes immediately while updating log
file (at `config.path.log`; use |:DepsShowLog| to review).
Otherwise show confirmation buffer with instructions on how to proceed.
Parameters ~
{names} `(table|nil)` Array of plugin names to update.
Default: all plugins from current session (see |MiniDeps.get_session()|).
{opts} `(table|nil)` Options. Possible fields:
- <force> `(boolean)` - whether to force update without confirmation.
Default: `false`.
- <offline> `(boolean)` - whether to skip downloading updates from sources.
Default: `false`.
------------------------------------------------------------------------------
*MiniDeps.clean()*
`MiniDeps.clean`({opts})
Clean plugins
- Compute absent plugins: not registered in current session
(see |MiniDeps.get_session()|) but present on disk in dedicated "pack/deps"
package (inside `config.path.package`).
- If cleaning is forced, delete all absent plugins from disk.
Otherwise show confirmation buffer with instructions on how to proceed.
Parameters ~
{opts} `(table|nil)` Options. Possible fields:
- <force> `(boolean)` - whether to force delete without confirmation.
Default: `false`.
------------------------------------------------------------------------------
*MiniDeps.snap_get()*
`MiniDeps.snap_get`()
Compute snapshot
Return ~
`(table)` A snapshot table: plugin names as keys and state as values.
All plugins in current session are processed.
------------------------------------------------------------------------------
*MiniDeps.snap_set()*
`MiniDeps.snap_set`({snap})
Apply snapshot
Notes:
- Checking out states from snapshot does not update session plugin spec
(`checkout` field in particular). Among others, it means that next call
to |MiniDeps.update()| might override the result of this function.
To make changes permanent, set `checkout` spec field to state from snapshot.
Parameters ~
{snap} `(table)` A snapshot table: plugin names as keys and state as values.
Only plugins in current session are processed.
------------------------------------------------------------------------------
*MiniDeps.snap_save()*
`MiniDeps.snap_save`({path})
Save snapshot
Parameters ~
{path} `(string|nil)` A valid path on disk where to write snapshot computed
with |MiniDeps.snap_get()|.
Default: `config.path.snapshot`.
------------------------------------------------------------------------------
*MiniDeps.snap_load()*
`MiniDeps.snap_load`({path})
Load snapshot file
Notes from |MiniDeps.snap_set()| also apply here.
Parameters ~
{path} `(string|nil)` A valid path on disk from where to read snapshot.
Default: `config.path.snapshot`.
------------------------------------------------------------------------------
*MiniDeps.get_session()*
`MiniDeps.get_session`()
Get session
Plugin is registered in current session if it either:
- Was added with |MiniDeps.add()| (preserving order of calls).
- Is a "start" plugin and present in 'runtimpath'.
Return ~
session `(table)` Array with specifications of all plugins registered in
current session.
------------------------------------------------------------------------------
*MiniDeps.now()*
`MiniDeps.now`({f})
Execute function now
Safely execute function immediately. Errors are shown with |vim.notify()|
later, after all queued functions (including with |MiniDeps.later()|)
are executed, thus not blocking execution of next code in file.
Assumed to be used as a first step during two-stage config execution to
load plugins immediately during startup. See |MiniDeps-overview|.
Parameters ~
{f} `(function)` Callable to execute.
------------------------------------------------------------------------------
*MiniDeps.later()*
`MiniDeps.later`({f})
Execute function later
Queue function to be safely executed later without blocking execution of
next code in file. All queued functions are guaranteed to be executed in
order they were added.
Errors are shown with |vim.notify()| after all queued functions are executed.
Assumed to be used as a second step during two-stage config execution to
load plugins "lazily" after startup. See |MiniDeps-overview|.
Parameters ~
{f} `(function)` Callable to execute.
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -0,0 +1,633 @@
*mini.diff* Work with diff hunks
*MiniDiff*
MIT License Copyright (c) 2024 Evgeni Chasnovski
==============================================================================
Features:
- Visualize difference between buffer text and its configurable reference
interactively (updates as you type). This is done per line showing whether
it is inside added, changed, or deleted part of difference (called hunk).
Visualization can be with customizable colored signs or line numbers.
- Special toggleable overlay view with more hunk details inside text area.
See |MiniDiff.toggle_overlay()|.
- Completely configurable per buffer source of reference text used to keep
it up to date and define interactions with it.
See |MiniDiff-source-specification|. By default uses buffer's file content
in Git index. See |MiniDiff.gen_source.git()|.
- Configurable mappings to manage diff hunks:
- Apply and reset hunks inside region (selected visually or with
a dot-repeatable operator).
- "Hunk range under cursor" textobject to be used as operator target.
- Navigate to first/previous/next/last hunk. See |MiniDiff.goto_hunk()|.
What it doesn't do:
- Provide functionality to work directly with Git outside of visualizing
and staging (applying) hunks with (default) Git source. In particular,
unstaging hunks is not supported. See |MiniDiff.gen_source.git()|.
Sources with more details:
- |MiniDiff-overview|
- |MiniDiff-source-specification|
- |MiniDiff-hunk-specification|
- |MiniDiff-diff-summary|
# Setup ~
This module needs a setup with `require('mini.diff').setup({})` (replace
`{}` with your `config` table). It will create global Lua table `MiniDiff`
which you can use for scripting or manually (with `:lua MiniDiff.*`).
See |MiniDiff.config| for `config` structure and default values.
You can override runtime config settings locally to buffer inside
`vim.b.minidiff_config` which should have same structure as
`MiniDiff.config`. See |mini.nvim-buffer-local-config| for more details.
# Comparisons ~
- 'lewis6991/gitsigns.nvim':
- Main inspiration for this module, so there are many similarities.
- Can display only Git hunks, while this module has extensible design.
- Provides more functionality to work with Git outside of hunks.
This module does not (by design).
# Highlight groups ~
* `MiniDiffSignAdd` - "add" hunk lines visualization.
* `MiniDiffSignChange` - "change" hunk lines visualization.
* `MiniDiffSignDelete` - "delete" hunk lines visualization.
* `MiniDiffOverAdd` - added text shown in overlay.
* `MiniDiffOverChange` - changed text shown in overlay.
* `MiniDiffOverContext` - context of changed text shown in overlay.
* `MiniDiffOverDelete` - deleted text shown in overlay.
To change any highlight group, modify it directly with |:highlight|.
# Disabling ~
To temporarily disable features without relying on |MiniDiff.disable()|,
set `vim.g.minidiff_disable` (globally) or `vim.b.minidiff_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.
------------------------------------------------------------------------------
*MiniDiff-overview*
# Diffs and hunks ~
The "diff" (short for "difference") is a result of computing how two text
strings differ from one another. This is done on per line basis, i.e. the
goal is to compute sequences of lines common to both files, interspersed
with groups of differing lines (called "hunks").
Although computing diff is a general concept (used on its own, in Git, etc.),
this module computes difference between current text in a buffer and some
reference text which is kept up to date specifically for that buffer.
For example, default reference text is computed as file content in Git index.
This can be customized in `config.source` (see |MiniDiff-source-specification|).
*MiniDiff-hunk-specification*
Hunk describes two sets (one from buffer text, one - from reference) of
consecutive lines which are different. In this module hunk is stored as
a table with the following fields:
- <buf_start> `(number)` - start of hunk buffer lines. First line is 1.
Can be 0 if first reference lines are deleted.
- <buf_count> `(number)` - number of consecutive buffer lines. Can be 0 in
case reference lines are deleted.
- <ref_start> `(number)` - start of hunk reference lines. First line is 1.
Can be 0 if lines are added before first reference line.
- <ref_count> `(number)` - number of consecutive reference lines. Can be 0 in
case buffer lines are added.
- <type> `(string)` - hunk type. Can be one of:
- "add" - lines are present in buffer but absent in reference.
- "change" - lines are present in both buffer and reference.
- "delete" - lines are absent in buffer but present in reference.
# Life cycle ~
- When entering proper (not already enabled, valid, showing text) buffer,
it is attempted to be enabled for diff processing.
- During enabling, attempt attaching the source. This should set up how
reference text is kept up to date.
- On every text change, diff computation is scheduled in debounced fashion
after customizable delay (200 ms by default).
- After the diff is computed, do the following:
- Update visualization based on configurable style: either by placing
colored text in sign column or coloring line numbers. Colors for both
styles are defined per hunk type in corresponding `MiniDiffSign*`
highlight group (see |MiniDiff|) and sign text for "sign" style can
be configured in `view.signs` of |MiniDiff.config|.
- Update overlay view (if it is enabled).
- Update `vim.b.minidiff_summary` and `vim.b.minidiff_summary_string`
buffer-local variables. These can be used, for example, in statusline.
*MiniDiff-update-event*
- Trigger `MiniDiffUpdated` `User` event. See |MiniDiff-diff-summary| for
example of how to use it.
Notes:
- Use |:edit| to reset (disable and re-enable) current buffer.
# Overlay ~
Along with basic visualization, there is a special view called "overlay".
Although it is meant for temporary overview of diff details and can be
manually toggled via |MiniDiff.toggle_overlay()|, text can be changed with
overlay reacting accordingly.
It shows more diff details inside text area:
- Added buffer lines are highlighted with `MiniDiffOverAdd` highlight group.
- Deleted reference lines are shown as virtual text and highlighted with
`MiniDiffOverDelete` highlight group.
- Changed reference lines are shown as virtual text and highlighted with
`MiniDiffOverChange` highlight group.
"Change" hunks with equal number of buffer and reference lines have special
treatment and show "word diff". Reference line is shown next to its buffer
counterpart and only changed parts of both lines are highlighted with
`MiniDiffOverChange`. The rest of reference line has `MiniDiffOverContext`
highlighting.
This usually is the case when `config.options.linematch` is enabled.
Notes:
- Word diff has non-zero context width. This means if changed characters
are close enough, whole range between them is also colored. This usually
reduces visual noise.
- Virtual lines above line 1 (like deleted or changed lines) need manual
scroll to become visible (with |CTRL-Y|).
# Mappings ~
This module provides mappings for common actions with diffs, like:
- Apply and reset hunks.
- "Hunk range under cursor" textobject.
- Go to first/previous/next/last hunk range.
Examples:
- `vip` followed by `gh` / `gH` applies/resets hunks inside current paragraph.
Same can be achieved in operator form `ghip` / `gHip`, which has the
advantage of being dot-repeatable (see |single-repeat|).
- `gh_` / `gH_` applies/resets current line (even if it is not a full hunk).
- `ghgh` / `gHgh` applies/resets hunk range under cursor.
- `dgh` deletes hunk range under cursor.
- `[H` / `[h` / `]h` / `]H` navigate cursor to the first / previous / next / last
hunk range of the current buffer.
Mappings for some functionality are assumed to be done manually.
See |MiniDiff.operator()|.
# Buffer-local variables ~
*MiniDiff-diff-summary*
Each enabled buffer has the following buffer-local variables which can be
used in custom statusline to show an overview of hunks in current buffer:
- `vim.b.minidiff_summary` is a table with the following fields:
- `source_name` - name of the source.
- `n_ranges` - number of hunk ranges (sequences of contiguous hunks).
- `add` - number of added lines.
- `change` - number of changed lines.
- `delete` - number of deleted lines.
- `vim.b.minidiff_summary_string` is a string representation of summary
with a fixed format. It is expected to be used as is. To achieve
different formatting, use `vim.b.minidiff_summary` to construct one.
The best way to do this is by overriding `vim.b.minidiff_summary_string`
in the callback for |MiniDiff-update-event| event: >lua
local format_summary = function(data)
local summary = vim.b[data.buf].minidiff_summary
local t = {}
if summary.add > 0 then table.insert(t, '+' .. summary.add) end
if summary.change > 0 then table.insert(t, '~' .. summary.change) end
if summary.delete > 0 then table.insert(t, '-' .. summary.delete) end
vim.b[data.buf].minidiff_summary_string = table.concat(t, ' ')
end
local au_opts = { pattern = 'MiniDiffUpdated', callback = format_summary }
vim.api.nvim_create_autocmd('User', au_opts)
<
------------------------------------------------------------------------------
*MiniDiff.setup()*
`MiniDiff.setup`({config})
Module setup
Parameters ~
{config} `(table|nil)` Module config table. See |MiniDiff.config|.
Usage ~
>lua
require('mini.diff').setup() -- use default config
-- OR
require('mini.diff').setup({}) -- replace {} with your config table
<
------------------------------------------------------------------------------
*MiniDiff.config*
`MiniDiff.config`
Module config
Default values:
>lua
MiniDiff.config = {
-- Options for how hunks are visualized
view = {
-- Visualization style. Possible values are 'sign' and 'number'.
-- Default: 'number' if line numbers are enabled, 'sign' otherwise.
style = vim.go.number and 'number' or 'sign',
-- Signs used for hunks with 'sign' view
signs = { add = '▒', change = '▒', delete = '▒' },
-- Priority of used visualization extmarks
priority = 199,
},
-- Source for how reference text is computed/updated/etc
-- Uses content from Git index by default
source = nil,
-- Delays (in ms) defining asynchronous processes
delay = {
-- How much to wait before update following every text change
text_change = 200,
},
-- Module mappings. Use `''` (empty string) to disable one.
mappings = {
-- Apply hunks inside a visual/operator region
apply = 'gh',
-- Reset hunks inside a visual/operator region
reset = 'gH',
-- Hunk range textobject to be used inside operator
-- Works also in Visual mode if mapping differs from apply and reset
textobject = 'gh',
-- Go to hunk range in corresponding direction
goto_first = '[H',
goto_prev = '[h',
goto_next = ']h',
goto_last = ']H',
},
-- Various options
options = {
-- Diff algorithm. See `:h vim.diff()`.
algorithm = 'histogram',
-- Whether to use "indent heuristic". See `:h vim.diff()`.
indent_heuristic = true,
-- The amount of second-stage diff to align lines (in Neovim>=0.9)
linematch = 60,
-- Whether to wrap around edges during hunk navigation
wrap_goto = false,
},
}
<
# View ~
`config.view` contains settings for how diff hunks are visualized.
Example of using custom signs: >lua
require('mini.diff').setup({
view = {
style = 'sign',
signs = { add = '+', change = '~', delete = '-' },
},
})
<
`view.style` is a string defining visualization style. Can be one of "sign"
(as a colored sign in a |sign-column|) or "number" (colored line number).
Default: "number" if |number| option is enabled, "sign" otherwise.
Note: with "sign" style it is usually better to have |signcolumn| always shown.
`view.signs` is a table with one or two character strings used as signs for
corresponding ("add", "change", "delete") hunks.
Default: all hunks use "▒" character resulting in a contiguous colored lines.
`view.priority` is a number with priority used for visualization and
overlay |extmarks|.
Default: 199 which is one less than `user` in |vim.highlight.priorities| to have
higher priority than automated extmarks but not as in user enabled ones.
*MiniDiff-source-specification*
# Source ~
`config.source` is a table defining how reference text is managed in
a particular buffer. It can have the following fields:
- <attach> `(function)` - callable which defines how and when reference text
should be updated inside a particular buffer. It is called
inside |MiniDiff.enable()| with a buffer identifier as a single argument.
Should execute logic which results into calling |MiniDiff.set_ref_text()|
when reference text for buffer needs to be updated. Like inside callback
for an |autocommand| or file watcher (see |watch-file|).
For example, default Git source watches when ".git/index" file is changed
and computes reference text as the one from Git index for current file.
Can return `false` to force buffer to not be enabled. If this can not be
inferred immediately (for example, due to asynchronous execution), should
call |MiniDiff.disable()| later to disable buffer.
No default value, should be always supplied.
- <name> `(string|nil)` - source name. String `"unknown"` is used if not supplied.
- <detach> `(function|nil)` - callable with cleanup action to be done when
buffer is disabled. It is called inside |MiniDiff.disable()| with a buffer
identifier as a single argument.
If not supplied, nothing is done during detaching.
- <apply_hunks> `(function|nil)` - callable which defines how hunks are applied.
It is called with buffer identifier as first argument and array of hunks
(see |MiniDiff-hunk-specification|) as second. It should eventually update
reference text: either by explicitly calling |MiniDiff.set_ref_text()| or
performing action triggering its call.
For example, default Git source computes patch based on the hunks and
applies it inside file's git repo.
If not supplied, applying hunks throws an error.
Default: |MiniDiff.gen_source.git()|.
# Delay ~
`config.delay` contains settings for delays in asynchronous processes.
`delay.text_change` is a number (in ms) defining how long to wait after latest
text change (in debounced fashion) before updating diff and visualization.
Default: 200.
# Mappings ~
`config.mappings` contains keys which are mapped during |MiniDiff.setup()|.
`mappings.apply` keys can be used to apply hunks inside visual/operator region.
What exactly "apply hunks" means depends on the source and its `apply_hunks()`.
For example, in default Git source it means stage hunks.
`mappings.reset` keys can be used to reset hunks inside visual/operator region.
Reset means replacing buffer text in region with corresponding reference text.
`mappings.textobject` keys define "hunk range under cursor" textobject
which can be used in Operator-pending mode as target for operator (like
|d|, |y|, apply/reset hunks, etc.). It is also set up in Visual mode if
keys do not conflict with `mappings.apply` and `mappings.reset`.
"Hunk range" is used in a sense that contiguous (back-to-back) hunks are
considered as parts of a same hunk range.
`mappings.goto_first` / `mappings.goto_prev` / `mappings.goto_next` /
`mappings.goto_last` keys can be used to navigate to first / previous / next /
last hunk range in the current buffer.
# Options ~
`config.options` contains various customization options.
`options.algorithm` is a string defining which diff algorithm to use.
Default: "histogram". See |vim.diff()| for possible values.
`options.indent_heuristic` is a boolean defining whether to use indent
heuristic for a (possibly) more naturally aligned hunks.
Default: `true`.
`options.linematch` is a number defining hunk size for which a second
stage diff is executed for a better aligned and more granular hunks.
Note: present only in Neovim>=0.9.
Default: 60. See |vim.diff()| and 'diffopt' for more details.
`options.wrap_goto` is a boolean indicating whether to wrap around edges during
hunk navigation (with |MiniDiff.goto_hunk()| or `goto_*` mappings). Like if
cursor is after the last hunk, going "next" will put cursor on the first hunk.
Default: `false`.
------------------------------------------------------------------------------
*MiniDiff.enable()*
`MiniDiff.enable`({buf_id})
Enable diff processing in buffer
Parameters ~
{buf_id} `(number)` Target buffer identifier. Default: 0 for current buffer.
------------------------------------------------------------------------------
*MiniDiff.disable()*
`MiniDiff.disable`({buf_id})
Disable diff processing in buffer
Parameters ~
{buf_id} `(number)` Target buffer identifier. Default: 0 for current buffer.
------------------------------------------------------------------------------
*MiniDiff.toggle()*
`MiniDiff.toggle`({buf_id})
Toggle diff processing in buffer
Enable if disabled, disable if enabled.
Parameters ~
{buf_id} `(number)` Target buffer identifier. Default: 0 for current buffer.
------------------------------------------------------------------------------
*MiniDiff.toggle_overlay()*
`MiniDiff.toggle_overlay`({buf_id})
Toggle overlay view in buffer
Parameters ~
{buf_id} `(number)` Target buffer identifier. Default: 0 for current buffer.
------------------------------------------------------------------------------
*MiniDiff.export()*
`MiniDiff.export`({format}, {opts})
Export hunks
Get and convert hunks from current/all buffers. Example of using it: >lua
-- Set quickfix list from all available hunks
vim.fn.setqflist(MiniDiff.export('qf'))
<
Parameters ~
{format} `(string)` Output format. Currently only `'qf'` value is supported.
{opts} `(table|nil)` Options. Possible fields:
- <scope> `(string)` - scope defining from which buffers to use hunks.
One of "all" (all enabled buffers) or "current".
Return ~
`(table)` Result of export. Depends on the `format`:
- If "qf", an array compatible with |setqflist()| and |setloclist()|.
------------------------------------------------------------------------------
*MiniDiff.get_buf_data()*
`MiniDiff.get_buf_data`({buf_id})
Get buffer data
Parameters ~
{buf_id} `(number)` Target buffer identifier. Default: 0 for current buffer.
Return ~
`(table|nil)` Table with buffer diff data or `nil` if buffer is not enabled.
Table has the following fields:
- <config> `(table)` - config used for this particular buffer.
- <hunks> `(table)` - array of hunks. See |MiniDiff-hunk-specification|.
- <overlay> `(boolean)` - whether an overlay view is shown.
- <ref_text> `(string|nil)` - current value of reference text. Lines are
separated with newline character (`'\n'`). Can be `nil` indicating that
reference text was not yet set (for example, if source did not yet react).
- <summary> `(table)` - overall diff summary. See |MiniDiff-diff-summary|.
------------------------------------------------------------------------------
*MiniDiff.set_ref_text()*
`MiniDiff.set_ref_text`({buf_id}, {text})
Set reference text for the buffer
Note: this will call |MiniDiff.enable()| for target buffer if it is not
already enabled.
Parameters ~
{buf_id} `(number)` Target buffer identifier. Default: 0 for current buffer.
{text} `(string|table)` New reference text. Either a string with `\n` used to
separate lines or array of lines. Use empty table to unset current
reference text (results into no hunks shown). Default: `{}`.
Note: newline character is appended at the end (if it is not there already)
for better diffs.
------------------------------------------------------------------------------
*MiniDiff.gen_source*
`MiniDiff.gen_source`
Generate builtin sources
This is a table with function elements. Call to actually get source.
Example of using |MiniDiff.gen_source.save()|: >lua
local diff = require('mini.diff')
diff.setup({ source = diff.gen_source.save() })
<
------------------------------------------------------------------------------
*MiniDiff.gen_source.git()*
`MiniDiff.gen_source.git`()
Git source
Default source. Uses file text from Git index as reference. This results in:
- "Add" hunks represent text present in current buffer, but not in index.
- "Change" hunks represent modified text already present in index.
- "Delete" hunks represent text deleted from index.
Applying hunks means staging, a.k.a adding to index.
Notes:
- Requires Git version at least 2.38.0.
- There is no capability for unstaging hunks. Use full Git client for that.
Return ~
`(table)` Source. See |MiniDiff-source-specification|.
------------------------------------------------------------------------------
*MiniDiff.gen_source.none()*
`MiniDiff.gen_source.none`()
"Do nothing" source
Allows buffers to be enabled while not setting any reference text.
Use this if the goal is to rely on manual |MiniDiff.set_ref_text()| calls.
Return ~
`(table)` Source. See |MiniDiff-source-specification|.
------------------------------------------------------------------------------
*MiniDiff.gen_source.save()*
`MiniDiff.gen_source.save`()
Latest save source
Uses text at latest save as the reference. This results into diff showing
difference after the latest save.
Return ~
`(table)` Source. See |MiniDiff-source-specification|.
------------------------------------------------------------------------------
*MiniDiff.do_hunks()*
`MiniDiff.do_hunks`({buf_id}, {action}, {opts})
Perform action on hunks in region
Compute hunks inside a target region (even for hunks only partially inside it)
and perform apply/reset/yank operation on them.
The "yank" action yanks all reference lines of target hunks into
a specified register (should be one of |registers|).
Notes:
- Whether hunk is inside a region is computed based on position of its
buffer lines.
- If "change" or "delete" is only partially inside a target region, all
reference lines are used in computed "intersection" hunk.
Used directly in `config.mappings.apply` and `config.mappings.reset`.
Usually there is no need to use this function manually.
See |MiniDiff.operator()| for how to set up a mapping for "yank".
Parameters ~
{buf_id} `(number)` Target buffer identifier. Default: 0 for current buffer.
{action} `(string)` One of "apply", "reset", "yank".
{opts} `(table|nil)` Options. Possible fields:
- <line_start> `(number)` - start line of the region. Default: 1.
- <line_end> `(number)` - start line of the region. Default: last buffer line.
- <register> `(string)` - register to yank reference lines into.
Default: |v:register|.
------------------------------------------------------------------------------
*MiniDiff.goto_hunk()*
`MiniDiff.goto_hunk`({direction}, {opts})
Go to hunk range in current buffer
Parameters ~
{direction} `(string)` One of "first", "prev", "next", "last".
{opts} `(table|nil)` Options. A table with fields:
- <n_times> `(number)` - Number of times to advance. Default: |v:count1|.
- <line_start> `(number)` - Line number to start from for directions
"prev" and "next". Default: cursor line.
- <wrap> `(boolean)` - Whether to wrap around edges.
Default: `options.wrap` value of the config.
------------------------------------------------------------------------------
*MiniDiff.operator()*
`MiniDiff.operator`({mode})
Perform action over region
Perform action over region defined by marks. Used in mappings.
Example of a mapping to yank reference lines of hunk range under cursor
(assuming default 'config.mappings.textobject'): >lua
local rhs = function() return MiniDiff.operator('yank') .. 'gh' end
vim.keymap.set('n', 'ghy', rhs, { expr = true, remap = true })
<
Parameters ~
{mode} `(string)` One of "apply", "reset", "yank", or the ones used in |g@|.
------------------------------------------------------------------------------
*MiniDiff.textobject()*
`MiniDiff.textobject`()
Select hunk range textobject
Selects all contiguous lines adjacent to cursor line which are in any (not
necessarily same) hunk (if cursor line itself is in hunk).
Used in default mappings.
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -0,0 +1,437 @@
*mini.doc* Generate Neovim help files
*MiniDoc*
MIT License Copyright (c) 2022 Evgeni Chasnovski
==============================================================================
Key design ideas:
- Keep documentation next to code by writing EmmyLua-like annotation
comments. They will be parsed as is, so formatting should follow built-in
guide in |help-writing|. However, custom hooks are allowed at many
generation stages for more granular management of output help file.
- Generation is done by processing a set of ordered files line by line.
Each line can either be considered as a part of documentation block (if
it matches certain configurable pattern) or not (considered to be an
"afterline" of documentation block). See |MiniDoc.generate()| for more
details.
- Processing is done by using nested data structures (section, block, file,
doc) describing certain parts of help file. See |MiniDoc-data-structures|
for more details.
- Project specific script can be written as plain Lua file with
configuratble path. See |MiniDoc.generate()| for more details.
What it doesn't do:
- It doesn't support markdown or other markup language inside annotations.
- It doesn't use treesitter in favor of Lua string manipulation for basic
tasks (parsing annotations, formatting, auto-generating tags, etc.). This
is done to manage complexity and be dependency free.
# Setup ~
This module needs a setup with `require('mini.doc').setup({})` (replace
`{}` with your `config` table). It will create global Lua table `MiniDoc`
which you can use for scripting or manually (with `:lua MiniDoc.*`).
See |MiniDoc.config| for available config settings.
You can override runtime config settings locally to buffer inside
`vim.b.minidoc_config` which should have same structure as `MiniDoc.config`.
See |mini.nvim-buffer-local-config| for more details.
To stop module from showing non-error feedback, set `config.silent = true`.
# Tips ~
- Some settings tips that might make writing annotation comments easier:
- Set up appropriate 'comments' for `lua` file type to respect
EmmyLua-like's `---` comment leader. Value `:---,:--` seems to work.
- Set up appropriate 'formatoptions' (see also |fo-table|). Consider
adding `j`, `n`, `q`, and `r` flags.
- Set up appropriate 'formatlistpat' to help auto-formatting lists (if
`n` flag is added to 'formatoptions'). One suggestion (not entirely
ideal) is a value `^\s*[0-9\-\+\*]\+[\.\)]*\s\+`. This reads as 'at
least one special character (digit, `-`, `+`, `*`) possibly followed
by some punctuation (`.` or `)`) followed by at least one space is a
start of list item'.
- Probably one of the most reliable resources for what is considered to be
best practice when using this module is this whole plugin. Look at source
code for the reference.
# Comparisons ~
- 'tjdevries/tree-sitter-lua':
- Its key design is to use treesitter grammar to parse both Lua code
and annotation comments. This makes it not easy to install,
customize, and support.
- It takes more care about automating output formatting (like auto
indentation and line width fit). This plugin leans more to manual
formatting with option to supply customized post-processing hooks.
------------------------------------------------------------------------------
*MiniDoc-data-structures*
Data structures
Data structures are basically arrays of other structures accompanied with
some fields (keys with data values) and methods (keys with function
values):
- `Section structure` is an array of string lines describing one aspect
(determined by section id like '@param', '@return', '@text') of an
annotation subject. All lines will be used directly in help file.
- `Block structure` is an array of sections describing one annotation
subject like function, table, concept.
- `File structure` is an array of blocks describing certain file on disk.
Basically, file is split into consecutive blocks: annotation lines go
inside block, non-annotation - inside `block_afterlines` element of info.
- `Doc structure` is an array of files describing a final help file. Each
string line from section (when traversed in depth-first fashion) goes
directly into output file.
All structures have these keys:
- Fields:
- `info` - contains additional information about current structure.
For more details see next section.
- `parent` - table of parent structure (if exists).
- `parent_index` - index of this structure in its parent's array. Useful
for adding to parent another structure near current one.
- `type` - string with structure type (section, block, file, doc).
- Methods (use them as `x:method(args)`):
- `insert(self, [index,] child)` - insert `child` to `self` at position
`index` (optional; if not supplied, child will be appended to end).
Basically, a `table.insert()`, but adds `parent` and `parent_index`
fields to `child` while properly updating `self`.
- `remove(self [,index])` - remove from `self` element at position
`index`. Basically, a `table.remove()`, but properly updates `self`.
- `has_descendant(self, predicate)` - whether there is a descendant
(structure or string) for which `predicate` returns `true`. In case of
success also returns the first such descendant as second value.
- `has_lines(self)` - whether structure has any lines (even empty ones)
to be put in output file. For section structures this is equivalent to
`#self`, but more useful for higher order structures.
- `clear_lines(self)` - remove all lines from structure. As a result,
this structure won't contribute to output help file.
Description of `info` fields per structure type:
- `Section`:
- `id` - captured section identifier. Can be empty string meaning no
identifier is captured.
- `line_begin` - line number inside file at which section begins (-1 if
not generated from file).
- `line_end` - line number inside file at which section ends (-1 if not
generated from file).
- `Block`:
- `afterlines` - array of strings which were parsed from file after
this annotation block (up until the next block or end of file).
Useful for making automated decisions about what is being documented.
- `line_begin` - line number inside file at which block begins (-1 if
not generated from file).
- `line_end` - line number inside file at which block ends (-1 if not
generated from file).
- `File`:
- `path` - absolute path to a file (`''` if not generated from file).
- `Doc`:
- `input` - array of input file paths (as in |MiniDoc.generate|).
- `output` - output path (as in |MiniDoc.generate|).
- `config` - configuration used (as in |MiniDoc.generate|).
------------------------------------------------------------------------------
*MiniDoc.setup()*
`MiniDoc.setup`({config})
Module setup
Parameters ~
{config} `(table|nil)` Module config table. See |MiniDoc.config|.
Usage ~
>lua
require('mini.doc').setup() -- use default config
-- OR
require('mini.doc').setup({}) -- replace {} with your config table
<
------------------------------------------------------------------------------
*MiniDoc.config*
`MiniDoc.config`
Module config
Default values:
>lua
MiniDoc.config = {
-- Function which extracts part of line used to denote annotation.
-- For more information see 'Notes' in |MiniDoc.config|.
annotation_extractor = function(l) return string.find(l, '^%-%-%-(%S*) ?') end,
-- Identifier of block annotation lines until first captured identifier
default_section_id = '@text',
-- Hooks to be applied at certain stage of document life cycle. Should
-- modify its input in place (and not return new one).
hooks = {
-- Applied to block before anything else
block_pre = --<function: infers header sections (tag and/or signature)>,
-- Applied to section before anything else
section_pre = --<function: replaces current aliases>,
-- Applied if section has specified captured id
sections = {
['@alias'] = --<function: registers alias in MiniDoc.current.aliases>,
['@class'] = --<function>,
['@diagnostic'] = --<function: ignores any section content>,
-- For most typical usage see |MiniDoc.afterlines_to_code|
['@eval'] = --<function: evaluates lines; replaces with their return>,
['@field'] = --<function>,
['@overload'] = --<function>,
['@param'] = --<function>,
['@private'] = --<function: registers block for removal>,
['@return'] = --<function>,
['@seealso'] = --<function>,
['@signature'] = --<function: formats signature of documented object>,
['@tag'] = --<function: turns its line in proper tag lines>,
['@text'] = --<function: purposefully does nothing>,
['@toc'] = --<function: clears all section lines>,
['@toc_entry'] = --<function: registers lines for table of contents>,
['@type'] = --<function>,
['@usage'] = --<function>,
},
-- Applied to section after all previous steps
section_post = --<function: currently does nothing>,
-- Applied to block after all previous steps
block_post = --<function: does many things>,
-- Applied to file after all previous steps
file = --<function: adds separator>,
-- Applied to doc after all previous steps
doc = --<function: adds modeline>,
-- Applied before output file is written. Takes lines array as argument.
write_pre = --<function: currently returns its input>,
-- Applied after output help file is written. Takes doc as argument.
write_post = --<function: various convenience actions>,
},
-- Path (relative to current directory) to script which handles project
-- specific help file generation (like custom input files, hooks, etc.).
script_path = 'scripts/minidoc.lua',
-- Whether to disable showing non-error feedback
silent = false,
}
<
# Notes ~
- `annotation_extractor` takes single string line as input. Output
describes what makes an input to be an annotation (if anything). It
should be similar to `string.find` with one capture group: start and end
of annotation indicator (whole part will be removed from help line) with
third value being string of section id (if input describes first line of
section; `nil` or empty string otherwise). Output should be `nil` if line
is not part of annotation.
Default value means that annotation line should:
- Start with `---` at first column.
- Any non-whitespace after `---` will be treated as new section id.
- Single whitespace at the start of main text will be ignored.
- Hooks are expected to be functions. Their default values might do many
things which might change over time, so for more information please look
at source code. Some more information can be found in
|MiniDoc.default_hooks|.
------------------------------------------------------------------------------
*MiniDoc.current*
`MiniDoc.current`
Table with information about current state of auto-generation
It is reset at the beginning and end of `MiniDoc.generate()`.
At least these keys are supported:
- {aliases} - table with keys being alias name and values - alias
description and single string (using `\n` to separate lines).
- {eval_section} - input section of `@eval` section hook. Can be used for
information about current block, etc.
- {toc} - array with table of contents entries. Each entry is a whole
`@toc_entry` section.
------------------------------------------------------------------------------
*MiniDoc.default_hooks*
`MiniDoc.default_hooks`
Default hooks
This is default value of `MiniDoc.config.hooks`. Use it if only a little
tweak is needed.
Some more insight about their behavior:
- Default inference of documented object metadata (tag and object signature
at the moment) is done in `block_pre`. Inference is based on string
pattern matching, so can lead to false results, although works in most
cases. It intentionally works only if first line after block has no
indentation and contains all necessary information to determine if
inference should happen.
- Hooks for sections describing some "variable-like" object ('@class',
'@field', '@param') automatically enclose first word in '{}'.
- Hooks for sections which supposed to have "type-like" data ('@field',
'@param', '@return', '@type') automatically enclose *first found*
"type-like" word and its neighbor characters in '`(<type>)`' (expect
false positives). Algorithm is far from being 100% correct, but seems to
work with present allowed type annotation. For allowed types see
https://github.com/sumneko/lua-language-server/wiki/EmmyLua-Annotations#types-and-type
or, better yet, look in source code of this module.
- Automated creation of table of contents (TOC) is done in the following way:
- Put section with `@toc_entry` id in the annotation block. Section's
lines will be registered as TOC entry.
- Put `@toc` section where you want to insert rendered table of
contents. TOC entries will be inserted on the left, references for
their respective tag section (only first, if present) on the right.
Render is done in default `doc` hook (because it should be done after
processing all files).
- The `write_post` hook executes some actions convenient for iterative
annotations writing:
- Generate `:helptags` for directory containing output file.
- Silently reload buffer containing output file (if such exists).
- Display notification message about result.
------------------------------------------------------------------------------
*MiniDoc.generate()*
`MiniDoc.generate`({input}, {output}, {config})
Generate help file
# Algorithm ~
- Main parameters for help generation are an array of input file paths and
path to output help file.
- Parse all inputs:
- For each file, lines are processed top to bottom in order to create an
array of documentation blocks. Each line is tested whether it is an
annotation by applying `MiniDoc.config.annotation_extractor`: if
anything is extracted, it is considered to be an annotation. Annotation
line goes to "current block" after removing extracted annotation
indicator, otherwise - to afterlines of "current block".
- Each block's annotation lines are processed top to bottom. If line had
captured section id, it is a first line of "current section" (first
block lines are allowed to not specify section id; by default it is
`@text`). All subsequent lines without captured section id go into
"current section".
- Apply structure hooks (they should modify its input in place, which is
possible due to 'table nature' of all inputs):
- Each block is processed by `MiniDoc.config.hooks.block_pre`. This is a
designated step for auto-generation of sections from described
annotation subject (like sections with id `@tag`, `@type`).
- Each section is processed by `MiniDoc.config.hooks.section_pre`.
- Each section is processed by corresponding
`MiniDoc.config.hooks.sections` function (table key equals to section
id). This is a step where most of formatting should happen (like
wrap first word of `@param` section with `{` and `}`, append empty
line to section, etc.).
- Each section is processed by `MiniDoc.config.hooks.section_post`.
- Each block is processed by `MiniDoc.config.hooks.block_post`. This is
a step for processing block after formatting is done (like add first
line with `----` delimiter).
- Each file is processed by `MiniDoc.config.hooks.file`. This is a step
for adding any file-related data (like add first line with `====`
delimiter).
- Doc is processed by `MiniDoc.config.hooks.doc`. This is a step for
adding any helpfile-related data (maybe like table of contents).
- Collect all strings from sections in depth-first fashion (equivalent to
nested "for all files -> for all blocks -> for all sections -> for all
strings -> add string to output"). Strings can have `\n` character
indicating start of new line.
- Modify collected strings with `MiniDoc.config.write_pre`. Takes strings
from previous step as input and should return array of strings.
- Write modified strings to output file.
- Execute `MiniDoc.config.write_post` hook. This is useful for showing some
feedback and making actions involving newly updated help file (like
generate tags, etc.).
# Project specific script ~
If all arguments have default `nil` values, first there is an attempt to
source project specific script. This is basically a `luafile
<MiniDoc.config.script_path>` with current Lua runtime while caching and
restoring current `MiniDoc.config`. Its successful execution stops any
further generation actions while error means proceeding generation as if no
script was found.
Typical script content might include definition of custom hooks, input and
output files with eventual call to `require('mini.doc').generate()` (with
or without arguments).
Parameters ~
{input} `(table|nil)` Array of file paths which will be processed in supplied
order. Default: all '.lua' files from current directory following by all
such files in these subdirectories: 'lua/', 'after/', 'colors/'. Note:
any 'init.lua' file is placed before other files from the same directory.
{output} `(string|nil)` Path for output help file. Default:
`doc/<current_directory>.txt` (designed to be used for generating help
file for plugin).
{config} `(table|nil)` Configuration overriding parts of |MiniDoc.config|.
Return ~
`(table)` Document structure which was generated and used for output
help file. In case `MiniDoc.config.script_path` was successfully used,
this is a return from the latest call of this function.
------------------------------------------------------------------------------
*MiniDoc.afterlines_to_code()*
`MiniDoc.afterlines_to_code`({struct})
Convert afterlines to code
This function is designed to be used together with `@eval` section to
automate documentation of certain values (notably default values of a
table). It processes afterlines based on certain directives and makes
output look like a Lua code block.
Most common usage is by adding the following section in your annotation: >
---@eval return MiniDoc.afterlines_to_code(MiniDoc.current.eval_section)
<
# Directives ~
Directives are special comments that are processed using Lua string pattern
capabilities (so beware of false positives). Each directive should be put
on its separate line. Supported directives:
- `--minidoc_afterlines_end` denotes a line at afterlines end. Only all
lines before it will be considered as afterlines. Useful if there is
extra code in afterlines which shouldn't be used.
- `--minidoc_replace_start <replacement>` and `--minidoc_replace_end`
denote lines between them which should be replaced with `<replacement>`.
Useful for manually changing what should be placed in output like in case
of replacing function body with something else.
Here is an example. Suppose having these afterlines: >lua
--minidoc_replace_start {
M.config = {
--minidoc_replace_end
param_one = 1,
--minidoc_replace_start param_fun = --<function>
param_fun = function(x)
return x + 1
end
--minidoc_replace_end
}
--minidoc_afterlines_end
return M
<
After adding `@eval` section those will be formatted as: >
{
param_one = 1,
param_fun = --<function>
}
<
Parameters ~
{struct} `(table)` Block or section structure which after lines will be
converted to code.
Return ~
`(string|nil)` Single string (using `\n` to separate lines) describing
afterlines as Lua code block in help file. If `nil`, input is not valid.
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -0,0 +1,805 @@
*mini.extra* Extra 'mini.nvim' functionality
*MiniExtra*
MIT License Copyright (c) 2023 Evgeni Chasnovski
==============================================================================
Extra useful functionality which is not essential enough for other 'mini.nvim'
modules to include directly.
Features:
- Various pickers for 'mini.pick':
- Built-in diagnostic (|MiniExtra.pickers.diagnostic()|).
- File explorer (|MiniExtra.pickers.explorer()|).
- Git branches/commits/files/hunks (|MiniExtra.pickers.git_hunks()|, etc.).
- Command/search/input history (|MiniExtra.pickers.history()|).
- LSP references/symbols/etc. (|MiniExtra.pickers.lsp()|).
- Tree-sitter nodes (|MiniExtra.pickers.treesitter()|).
- And much more.
See |MiniExtra.pickers| for more.
- Various textobject specifications for 'mini.ai'. See |MiniExtra.gen_ai_spec|.
- Various highlighters for 'mini.hipatterns'. See |MiniExtra.gen_highlighter|.
Notes:
- This module requires only those 'mini.nvim' modules which are needed for
a particular functionality: 'mini.pick' for pickers, etc.
# Setup ~
This module needs a setup with `require('mini.extra').setup({})` (replace
`{}` with your `config` table). It will create global Lua table `MiniExtra`
which you can use for scripting or manually (with `:lua MiniExtra.*`).
See |MiniExtra.config| for `config` structure and default values.
This module doesn't have runtime options, so using `vim.b.miniextra_config`
will have no effect here.
# Comparisons ~
- 'nvim-telescope/telescope.nvim':
- With |MiniExtra.pickers|, 'mini.pick' is reasonably on par when it comes
to built-in pickers.
- 'ibhagwan/fzf-lua':
- Same as 'nvim-telescope/telescope.nvim'.
------------------------------------------------------------------------------
*MiniExtra.setup()*
`MiniExtra.setup`({config})
Module setup
Parameters ~
{config} `(table|nil)` Module config table. See |MiniExtra.config|.
Usage ~
>lua
require('mini.extra').setup() -- use default config
-- OR
require('mini.extra').setup({}) -- replace {} with your config table
<
------------------------------------------------------------------------------
*MiniExtra.config*
`MiniExtra.config`
Module config
Default values:
>lua
MiniExtra.config = {}
<
------------------------------------------------------------------------------
*MiniExtra.gen_ai_spec*
`MiniExtra.gen_ai_spec`
'mini.ai' textobject specification generators
This is a table with function elements. Call to actually get specification.
Assumed to be used as part of |MiniAi.setup()|. Example: >lua
local gen_ai_spec = require('mini.extra').gen_ai_spec
require('mini.ai').setup({
custom_textobjects = {
B = gen_ai_spec.buffer(),
D = gen_ai_spec.diagnostic(),
I = gen_ai_spec.indent(),
L = gen_ai_spec.line(),
N = gen_ai_spec.number(),
},
})
<
------------------------------------------------------------------------------
*MiniExtra.gen_ai_spec.buffer()*
`MiniExtra.gen_ai_spec.buffer`()
Current buffer textobject
Notes:
- `a` textobject selects all lines in a buffer.
- `i` textobject selects all lines except blank lines at start and end.
Return ~
`(function)` Function implementing |MiniAi-textobject-specification|.
------------------------------------------------------------------------------
*MiniExtra.gen_ai_spec.diagnostic()*
`MiniExtra.gen_ai_spec.diagnostic`({severity})
Current buffer diagnostic textobject
Notes:
- Both `a` and `i` textobjects return |vim.diagnostic.get()| output for the
current buffer. It is modified to fit |MiniAi-textobject-specification|.
Parameters ~
{severity} `(any)` Which severity to use. Forwarded to |vim.diagnostic.get()|.
Default: `nil` to use all diagnostic entries.
Return ~
`(function)` Function implementing |MiniAi-textobject-specification|.
------------------------------------------------------------------------------
*MiniExtra.gen_ai_spec.indent()*
`MiniExtra.gen_ai_spec.indent`()
Current buffer indent scopes textobject
Indent scope is a set of consecutive lines with the following properties:
- Lines above first and below last are non-blank. They are called borders.
- There is at least one non-blank line in a set.
- All non-blank lines between borders have strictly greater indent
(perceived leading space respecting |tabstop|) than either of borders.
Notes:
- `a` textobject selects scope including borders.
- `i` textobject selects the scope charwise.
- Differences with |MiniIndentscope.textobject|:
- This textobject always treats blank lines on top and bottom of `i`
textobject as part of it, while 'mini.indentscope' can configure that.
- This textobject can select non-covering scopes, while 'mini.indentscope'
can not (by design).
- In this textobject scope computation is done only by "casting rays" from
top to bottom and not in both ways as in 'mini.indentscope'.
This works in most common scenarios and doesn't work only if indent of
of the bottom border is expected to be larger than the top.
Return ~
`(function)` Function implementing |MiniAi-textobject-specification|.
It returns array of regions representing all indent scopes in the buffer
ordered increasingly by the start line.
------------------------------------------------------------------------------
*MiniExtra.gen_ai_spec.line()*
`MiniExtra.gen_ai_spec.line`()
Current line textobject
Notes:
- `a` textobject selects whole line.
- `i` textobject selects line after initial indent.
Return ~
`(function)` Function implementing |MiniAi-textobject-specification|.
------------------------------------------------------------------------------
*MiniExtra.gen_ai_spec.number()*
`MiniExtra.gen_ai_spec.number`()
Number textobject
Notes:
- `a` textobject selects a whole number possibly preceded with "-" and
possibly followed by decimal part (dot and digits).
- `i` textobject selects consecutive digits.
Return ~
`(function)` Function implementing |MiniAi-textobject-specification|.
------------------------------------------------------------------------------
*MiniExtra.gen_highlighter*
`MiniExtra.gen_highlighter`
'mini.hipatterns' highlighter generators
This is a table with function elements. Call to actually get specification.
Assumed to be used as part of |MiniHipatterns.setup()|. Example: >lua
local hi_words = require('mini.extra').gen_highlighter.words
require('mini.hipatterns').setup({
highlighters = {
todo = hi_words({ 'TODO', 'Todo', 'todo' }, 'MiniHipatternsTodo'),
},
})
<
------------------------------------------------------------------------------
*MiniExtra.gen_highlighter.words()*
`MiniExtra.gen_highlighter.words`({words}, {group}, {extmark_opts})
Highlight words
Notes:
- Words should start and end with alphanumeric symbol (latin letter or digit).
- Words will be highlighted only in full and not if part bigger word, i.e.
there should not be alphanumeric symbol before and after it.
Parameters ~
{words} `(table)` Array of words to highlight. Will be matched as is, not
as Lua pattern.
{group} `(string|function)` Proper `group` field for `highlighter`.
See |MiniHipatterns.config|.
{extmark_opts} `(any)` Proper `extmark_opts` field for `highlighter`.
See |MiniHipatterns.config|.
------------------------------------------------------------------------------
*MiniExtra.pickers*
`MiniExtra.pickers`
'mini.pick' pickers
A table with |MiniPick| pickers (which is a hard dependency).
Notes:
- All have the same signature:
- <local_opts> - optional table with options local to picker.
- <opts> - optional table with options forwarded to |MiniPick.start()|.
- All of them are automatically registered in |MiniPick.registry|.
- All use default versions of |MiniPick-source.preview|, |MiniPick-source.choose|,
and |MiniPick-source.choose_marked| if not stated otherwise.
Shown text and |MiniPick-source.show| are targeted to the picked items.
Examples of usage:
- As Lua code: `MiniExtra.pickers.buf_lines()`.
- With |:Pick| command: `:Pick buf_lines scope='current'`
Note: this requires calling |MiniExtra.setup()|.
------------------------------------------------------------------------------
*MiniExtra.pickers.buf_lines()*
`MiniExtra.pickers.buf_lines`({local_opts}, {opts})
Buffer lines picker
Pick from buffer lines. Notes:
- Loads all target buffers which are currently unloaded.
Parameters ~
{local_opts} `(table|nil)` Options defining behavior of this particular picker.
Possible fields:
- <scope> `(string)` - one of "all" (normal listed buffers) or "current".
Default: "all".
{opts} `(table|nil)` Options forwarded to |MiniPick.start()|.
Return ~
`(any)` Output of the called picker.
------------------------------------------------------------------------------
*MiniExtra.pickers.commands()*
`MiniExtra.pickers.commands`({local_opts}, {opts})
Neovim commands picker
Pick from Neovim built-in (|ex-commands|) and |user-commands|.
Notes:
- Preview shows information about the command (if available).
- Choosing either executes command (if reliably known that it doesn't need
arguments) or populates Command line with the command.
Parameters ~
{local_opts} `(table|nil)` Options defining behavior of this particular picker.
Not used at the moment.
{opts} `(table|nil)` Options forwarded to |MiniPick.start()|.
Return ~
`(any)` Output of the called picker.
------------------------------------------------------------------------------
*MiniExtra.pickers.diagnostic()*
`MiniExtra.pickers.diagnostic`({local_opts}, {opts})
Built-in diagnostic picker
Pick from |vim.diagnostic| using |vim.diagnostic.get()|.
Parameters ~
{local_opts} `(table|nil)` Options defining behavior of this particular picker.
Possible fields:
- <get_opts> `(table)` - options for |vim.diagnostic.get()|. Can be used
to limit severity or namespace. Default: `{}`.
- <scope> `(string)` - one of "all" (available) or "current" (buffer).
Default: "all".
- <sort_by> `(string)` - sort priority. One of "severity", "path", "none".
Default: "severity".
{opts} `(table|nil)` Options forwarded to |MiniPick.start()|.
Return ~
`(any)` Output of the called picker.
------------------------------------------------------------------------------
*MiniExtra.pickers.explorer()*
`MiniExtra.pickers.explorer`({local_opts}, {opts})
File explorer picker
Explore file system and open file.
Notes:
- Choosing a directory navigates inside it, changing picker's items and
current working directory.
- Query and preview work as usual (not only `move_next`/`move_prev` can be used).
- Preview works for any item.
Examples ~
- `MiniExtra.pickers.explorer()`
- `:Pick explorer cwd='..'` - open explorer in parent directory.
Parameters ~
{local_opts} `(table|nil)` Options defining behavior of this particular picker.
Possible fields:
- <cwd> `(string)` - initial directory to explore. Should be a valid
directory path. Default: `nil` for |current-directory|.
- <filter> `(function)` - callable predicate to filter items to show.
Will be called for every item and should return `true` if it should be
shown. Each item is a table with the following fields:
- <fs_type> `(string)` - path type. One of "directory" or "file".
- <path> `(string)` - item path.
- <text> `(string)` - shown text (path's basename).
- <sort> `(function)` - callable item sorter. Will be called with array
of items (each element with structure as described above) and should
return sorted array of items.
{opts} `(table|nil)` Options forwarded to |MiniPick.start()|.
Return ~
`(any)` Output of the called picker.
------------------------------------------------------------------------------
*MiniExtra.pickers.git_branches()*
`MiniExtra.pickers.git_branches`({local_opts}, {opts})
Git branches picker
Pick from Git branches using `git branch`.
Notes:
- Requires executable `git`.
- Requires target path to be part of git repository.
- Present for exploration and navigation purposes. Doing any Git operations
is suggested to be done in a dedicated Git client and is not planned.
- On choose opens scratch buffer with branch's history.
Examples ~
- `MiniExtra.pickers.git_branches({ scope = 'local' })` - local branches of
the |current-directory| parent Git repository.
- `:Pick git_branches path='%'` - all branches of the current file parent
Git repository.
Parameters ~
{local_opts} `(table|nil)` Options defining behavior of this particular picker.
Possible fields:
- <path> `(string|nil)` - target path for Git operation (if required). Also
used to find Git repository inside which to construct items.
Default: `nil` for root of Git repository containing |current-directory|.
- <scope> `(string)` - branch scope to show. One of "all", "local", "remotes".
Default: "all".
{opts} `(table|nil)` Options forwarded to |MiniPick.start()|.
Return ~
`(any)` Output of the called picker.
------------------------------------------------------------------------------
*MiniExtra.pickers.git_commits()*
`MiniExtra.pickers.git_commits`({local_opts}, {opts})
Git commits picker
Pick from Git commits using `git log`.
Notes:
- Requires executable `git`.
- Requires target path to be part of git repository.
- Present for exploration and navigation purposes. Doing any Git operations
is suggested to be done in a dedicated Git client and is not planned.
- On choose opens scratch buffer with commit's diff.
Examples ~
- `MiniExtra.pickers.git_commits()` - all commits from parent Git
repository of |current-directory|.
- `MiniExtra.pickers.git_commits({ path = 'subdir' })` - commits affecting
files from 'subdir' subdirectory.
- `:Pick git_commits path='%'` commits affecting current file.
Parameters ~
{local_opts} `(table|nil)` Options defining behavior of this particular picker.
Possible fields:
- <path> `(string|nil)` - target path for Git operation (if required). Also
used to find Git repository inside which to construct items.
Default: `nil` for root of Git repository containing |current-directory|.
{opts} `(table|nil)` Options forwarded to |MiniPick.start()|.
Return ~
`(any)` Output of the called picker.
------------------------------------------------------------------------------
*MiniExtra.pickers.git_files()*
`MiniExtra.pickers.git_files`({local_opts}, {opts})
Git files picker
Pick from Git files using `git ls-files`.
Notes:
- Requires executable `git`.
- Requires target path to be part of git repository.
- Present for exploration and navigation purposes. Doing any Git operations
is suggested to be done in a dedicated Git client and is not planned.
Examples ~
- `MiniExtra.pickers.git_files({ scope = 'ignored' })` - ignored files from
parent Git repository of |current-directory|.
- `:Pick git_files path='subdir' scope='modified'` - files from 'subdir'
subdirectory which are ignored by Git.
Parameters ~
{local_opts} `(table|nil)` Options defining behavior of this particular picker.
Possible fields:
- <path> `(string|nil)` - target path for Git operation (if required). Also
used to find Git repository inside which to construct items.
Default: `nil` for root of Git repository containing |current-directory|.
- <scope> `(string)` - files scope to show. One of
- "tracked" (`--cached` Git flag).
- "modified" (`--modified` Git flag).
- "untracked" (`--others` Git flag).
- "ignored" (`--ignored` Git flag).
- "deleted" (`--deleted` Git flag).
Default: "tracked".
{opts} `(table|nil)` Options forwarded to |MiniPick.start()|.
Return ~
`(any)` Output of the called picker.
------------------------------------------------------------------------------
*MiniExtra.pickers.git_hunks()*
`MiniExtra.pickers.git_hunks`({local_opts}, {opts})
Git hunks picker
Pick from Git hunks using `git diff`.
Notes:
- Requires executable `git`.
- Requires target path to be part of git repository.
- Present for exploration and navigation purposes. Doing any Git operations
is suggested to be done in a dedicated Git client and is not planned.
- On choose navigates to hunk's first change.
Examples ~
- `MiniExtra.pickers.git_hunks({ scope = 'staged' })` - staged hunks from
parent Git repository of |current-directory|.
- `:Pick git_hunks path='%' n_context=0` - hunks from current file computed
with no context.
Parameters ~
{local_opts} `(table|nil)` Options defining behavior of this particular picker.
Possible fields:
- <n_context> `(number)` - number of context lines to show in hunk's preview.
Default: 3.
- <path> `(string|nil)` - target path for Git operation (if required). Also
used to find Git repository inside which to construct items.
Default: `nil` for root of Git repository containing |current-directory|.
- <scope> `(string)` - hunks scope to show. One of "unstaged" or "staged".
Default: "unstaged".
{opts} `(table|nil)` Options forwarded to |MiniPick.start()|.
Return ~
`(any)` Output of the called picker.
------------------------------------------------------------------------------
*MiniExtra.pickers.hipatterns()*
`MiniExtra.pickers.hipatterns`({local_opts}, {opts})
Matches from 'mini.hipatterns' picker
Pick from |MiniHipatterns| matches using |MiniHipatterns.get_matches()|.
Notes:
- Requires 'mini.hipatterns'.
- Highlighter identifier is highlighted with its highlight group.
Parameters ~
{local_opts} `(table|nil)` Options defining behavior of this particular picker.
Possible fields:
- <scope> `(string)` - one of "all" (buffers with enabled 'mini.hipatterns')
or "current" (buffer). Default: "all".
- <highlighters> `(table|nil)` - highlighters for which to find matches.
Forwarded to |MiniHipatterns.get_matches()|. Default: `nil`.
{opts} `(table|nil)` Options forwarded to |MiniPick.start()|.
Return ~
`(any)` Output of the called picker.
------------------------------------------------------------------------------
*MiniExtra.pickers.history()*
`MiniExtra.pickers.history`({local_opts}, {opts})
Neovim history picker
Pick from output of |:history|.
Notes:
- Has no preview.
- Choosing action depends on scope:
- For "cmd" / ":" scopes, the command is executed.
- For "search" / "/" / "?" scopes, search is redone.
- For other scopes nothing is done (but chosen item is still returned).
Examples ~
- Command history: `MiniExtra.pickers.history({ scope = ':' })`
- Search history: `:Pick history scope='/'`
Parameters ~
{local_opts} `(table|nil)` Options defining behavior of this particular picker.
Possible fields:
- <scope> `(string)` - any allowed {name} flag of |:history| command.
Note: word abbreviations are not allowed. Default: "all".
{opts} `(table|nil)` Options forwarded to |MiniPick.start()|.
Return ~
`(any)` Output of the called picker.
------------------------------------------------------------------------------
*MiniExtra.pickers.hl_groups()*
`MiniExtra.pickers.hl_groups`({local_opts}, {opts})
Highlight groups picker
Pick and preview highlight groups.
Notes:
- Item line is colored with same highlight group it represents.
- Preview shows highlight's definition (as in |:highlight| with {group-name}).
- Choosing places highlight definition in Command line to update and apply.
Parameters ~
{local_opts} `(table|nil)` Options defining behavior of this particular picker.
Not used at the moment.
{opts} `(table|nil)` Options forwarded to |MiniPick.start()|.
Return ~
`(any)` Output of the called picker.
------------------------------------------------------------------------------
*MiniExtra.pickers.keymaps()*
`MiniExtra.pickers.keymaps`({local_opts}, {opts})
Neovim keymaps picker
Pick and preview data about Neovim keymaps.
Notes:
- Item line contains data about keymap mode, whether it is buffer local, its
left hand side, and inferred description.
- Preview shows keymap data or callback source (if present and reachable).
- Choosing emulates pressing the left hand side of the keymap.
Parameters ~
{local_opts} `(table|nil)` Options defining behavior of this particular picker.
Possible fields:
- <mode> `(string)` - modes to show. One of "all" or appropriate mode
for |nvim_set_keymap()|. Default: "all".
- <scope> `(string)` - scope to show. One of "all", "global", "buf".
Default: "all".
{opts} `(table|nil)` Options forwarded to |MiniPick.start()|.
Return ~
`(any)` Output of the called picker.
------------------------------------------------------------------------------
*MiniExtra.pickers.list()*
`MiniExtra.pickers.list`({local_opts}, {opts})
Neovim lists picker
Pick and navigate to elements of the following Neovim lists:
- |quickfix| list.
- |location-list| of current window.
- |jumplist|.
- |changelist|.
Note: it requires explicit `scope`.
Examples ~
- `MiniExtra.pickers.list({ scope = 'quickfix' })` - quickfix list.
- `:Pick list scope='jump'` - jump list.
Parameters ~
{local_opts} `(table|nil)` Options defining behavior of this particular picker.
Possible fields:
- <scope> `(string)` - type of list to show. One of "quickfix", "location",
"jump", "change". Default: `nil` which means explicit scope is needed.
{opts} `(table|nil)` Options forwarded to |MiniPick.start()|.
Return ~
`(any)` Output of the called picker.
------------------------------------------------------------------------------
*MiniExtra.pickers.lsp()*
`MiniExtra.pickers.lsp`({local_opts}, {opts})
LSP picker
Pick and navigate with LSP methods.
Notes:
- Needs an explicit scope from a list of supported ones:
- "declaration".
- "definition".
- "document_symbol".
- "implementation".
- "references".
- "type_definition".
- "workspace_symbol".
- Directly relies on `vim.lsp.buf` methods which support |lsp-on-list-handler|.
In particular, it means that picker is started only if LSP server returns
list of locations and not a single location.
- Doesn't return anything due to async nature of `vim.lsp.buf` methods.
- Requires set up |mini.icons| to show extra icons and highlighting in
"document_symbol" and "workspace_symbol" scopes.
Examples ~
- `MiniExtra.pickers.lsp({ scope = 'references' })` - references of the symbol
under cursor.
- `:Pick lsp scope='document_symbol'` - symbols in current file.
Parameters ~
{local_opts} `(table)` Options defining behavior of this particular picker.
Possible fields:
- <scope> `(string)` - LSP method to use. One of the supported ones (see
list above). Default: `nil` which means explicit scope is needed.
- <symbol_query> `(string)` - query for |vim.lsp.buf.workspace_symbol()|.
Default: empty string for all symbols (according to LSP specification).
{opts} `(table|nil)` Options forwarded to |MiniPick.start()|.
Return ~
`(nil)` Nothing is returned.
------------------------------------------------------------------------------
*MiniExtra.pickers.marks()*
`MiniExtra.pickers.marks`({local_opts}, {opts})
Neovim marks picker
Pick and preview position of Neovim |mark|s.
Parameters ~
{local_opts} `(table|nil)` Options defining behavior of this particular picker.
Possible fields:
- <scope> `(string)` - scope to show. One of "all", "global", "buf".
Default: "all".
{opts} `(table|nil)` Options forwarded to |MiniPick.start()|.
Return ~
`(any)` Output of the called picker.
------------------------------------------------------------------------------
*MiniExtra.pickers.oldfiles()*
`MiniExtra.pickers.oldfiles`({local_opts}, {opts})
Old files picker
Pick from |v:oldfiles| entries representing readable files.
Parameters ~
{local_opts} `(table|nil)` Options defining behavior of this particular picker.
Possible fields:
- <current_dir> `(boolean)` - whether to return files only from current
working directory and its subdirectories. Default: `false`.
{opts} `(table|nil)` Options forwarded to |MiniPick.start()|.
Return ~
`(any)` Output of the called picker.
------------------------------------------------------------------------------
*MiniExtra.pickers.options()*
`MiniExtra.pickers.options`({local_opts}, {opts})
Neovim options picker
Pick and preview data about Neovim options.
Notes:
- Item line is colored based on whether it was set (dimmed if wasn't).
- Preview shows option value in target window and its general information.
- Choosing places option name in Command line to update and apply.
Parameters ~
{local_opts} `(table|nil)` Options defining behavior of this particular picker.
Possible fields:
- <scope> `(string)` - options to show. One of "all", "global", "win", "buf".
Default: "all".
{opts} `(table|nil)` Options forwarded to |MiniPick.start()|.
Return ~
`(any)` Output of the called picker.
------------------------------------------------------------------------------
*MiniExtra.pickers.registers()*
`MiniExtra.pickers.registers`({local_opts}, {opts})
Neovim registers picker
Pick from Neovim |registers|.
Notes:
- There is no preview (all information is in the item's text).
- Choosing pastes content of a register: with |i_CTRL-R| in Insert mode,
|c_CTRL-R| in Command-line mode, and |P| otherwise.
Expression register |quote=| is reevaluated (if present) and pasted.
Parameters ~
{local_opts} `(table|nil)` Options defining behavior of this particular picker.
Not used at the moment.
{opts} `(table|nil)` Options forwarded to |MiniPick.start()|.
Return ~
`(any)` Output of the called picker.
------------------------------------------------------------------------------
*MiniExtra.pickers.spellsuggest()*
`MiniExtra.pickers.spellsuggest`({local_opts}, {opts})
Neovim spell suggestions picker
Pick and apply spell suggestions.
Notes:
- No preview is available.
- Choosing replaces current word (|<cword>|) with suggestion.
Parameters ~
{local_opts} `(table|nil)` Options defining behavior of this particular picker.
Possible fields:
- <n_suggestions> `(number)` - number of spell suggestions. Default: 25.
{opts} `(table|nil)` Options forwarded to |MiniPick.start()|.
Return ~
`(any)` Output of the called picker.
------------------------------------------------------------------------------
*MiniExtra.pickers.treesitter()*
`MiniExtra.pickers.treesitter`({local_opts}, {opts})
Tree-sitter nodes picker
Pick and navigate to |treesitter| nodes of current buffer.
Notes:
- Requires active tree-sitter parser in the current buffer.
Parameters ~
{local_opts} `(table|nil)` Options defining behavior of this particular picker.
Not used at the moment.
{opts} `(table|nil)` Options forwarded to |MiniPick.start()|.
Return ~
`(any)` Output of the called picker.
------------------------------------------------------------------------------
*MiniExtra.pickers.visit_paths()*
`MiniExtra.pickers.visit_paths`({local_opts}, {opts})
Visit paths from 'mini.visits' picker
Pick paths from |MiniVisits| using |MiniVisits.list_paths()|.
Notes:
- Requires 'mini.visits'.
Examples ~
- `MiniExtra.pickers.visit_paths()` - visits registered for |current-directory|
and ordered by "robust frecency".
- `:Pick visit_paths cwd='' recency_weight=1 filter='core'` - all visits with
"core" label ordered from most to least recent.
Parameters ~
{local_opts} `(table|nil)` Options defining behavior of this particular picker.
Possible fields:
- <cwd> `(string)` - forwarded to |MiniVisits.list_paths()|.
Default: `nil` to get paths registered for |current-directory|.
- <filter> `(function|string)` - forwarded to |MiniVisits.list_paths()|.
Default: `nil` to use all paths.
- <preserve_order> `(boolean)` - whether to preserve original order
during query. Default: `false`.
- <recency_weight> `(number)` - forwarded to |MiniVisits.gen_sort.default()|.
Default: 0.5 to use "robust frecency" sorting.
- <sort> `(function)` - forwarded to |MiniVisits.list_paths()|.
Default: `nil` to use "robust frecency".
Note: if supplied, has precedence over `recency_weight`.
{opts} `(table|nil)` Options forwarded to |MiniPick.start()|.
Return ~
`(any)` Output of the called picker.
------------------------------------------------------------------------------
*MiniExtra.pickers.visit_labels()*
`MiniExtra.pickers.visit_labels`({local_opts}, {opts})
Visit labels from 'mini.visits' picker
Pick labels from |MiniVisits| using |MiniVisits.list_labels()|
and |MiniVisits.list_paths()|.
Notes:
- Requires 'mini.visits'.
- Preview shows target visit paths filtered to those having previewed label.
- Choosing essentially starts |MiniExtra.pickers.visit_paths()| for paths
with the chosen label.
Examples ~
- `MiniExtra.pickers.visit_labels()` - labels from visits registered
for |current-directory|.
- `:Pick visit_labels cwd=''` - labels from all visits.
Parameters ~
{local_opts} `(table|nil)` Options defining behavior of this particular picker.
Possible fields:
- <cwd> `(string)` - forwarded to |MiniVisits.list_labels()|.
Default: `nil` to get labels from visits registered for |current-directory|.
- <filter> `(function|string)` - forwarded to |MiniVisits.list_labels()|.
Default: `nil` to use all visits.
- <path> `(string)` - forwarded to |MiniVisits.list_labels()|.
Default: `""` to get labels from all visits for target `cwd`.
- <sort> `(function)` - forwarded to |MiniVisits.list_paths()| for
preview and choose. Default: `nil` to use "robust frecency".
{opts} `(table|nil)` Options forwarded to |MiniPick.start()|.
Return ~
Chosen path.
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -0,0 +1,859 @@
*mini.files* Navigate and manipulate file system
*MiniFiles*
MIT License Copyright (c) 2023 Evgeni Chasnovski
==============================================================================
Features:
- Navigate file system using column view (Miller columns) to display nested
directories. See |MiniFiles-navigation| for overview.
- Opt-in preview of file or directory under cursor.
- Manipulate files and directories by editing text buffers: create, delete,
copy, rename, move. See |MiniFiles-manipulation| for overview.
- Use as default file explorer instead of |netrw|.
- Configurable:
- Filter/prefix/sort of file system entries.
- Mappings used for common explorer actions.
- UI options: whether to show preview of file/directory under cursor, etc.
What it doesn't do:
- Try to be replacement of system file explorer. It is mostly designed to
be used within Neovim to quickly explore file system structure, open
files, and perform some quick file system edits.
- Work on remote locations. Only local file system is supported.
- Provide built-in interactive toggle of content `filter` and `sort`.
See |MiniFiles-examples| for some common examples.
- Provide out of the box extra information like git or diagnostic status.
This can be achieved by setting |extmarks| on appropriate event(s)
(see |MiniFiles-events|)
Notes:
- This module is written and thoroughly tested on Linux. Support for other
platform/OS (like Windows or MacOS) is a goal, but there is no guarantee.
- Works on all supported versions but using Neovim>=0.9 is recommended.
- This module silently reacts to not enough permissions:
- In case of missing file, check its or its parent read permissions.
- In case of no manipulation result, check write permissions.
# Dependencies ~
Suggested dependencies (provide extra functionality, will work without them):
- Enabled |MiniIcons| module to show icons near file/directory names.
Falls back to 'nvim-tree/nvim-web-devicons' plugin or uses default icons.
# Setup ~
This module needs a setup with `require('mini.files').setup({})` (replace
`{}` with your `config` table). It will create global Lua table `MiniFiles`
which you can use for scripting or manually (with `:lua MiniFiles.*`).
See |MiniFiles.config| for available config settings.
You can override runtime config settings (like mappings or window options)
locally to buffer inside `vim.b.minifiles_config` which should have same
structure as `MiniFiles.config`. See |mini.nvim-buffer-local-config| for
more details.
# Comparisons ~
- 'nvim-tree/nvim-tree.lua':
- Provides tree view of file system, while this module uses column view.
- File system manipulation is done with custom set of mappings for each
action, while this module is designed to do that by editing text.
- Has more out of the box functionality with extra configuration, while
this module has not (by design).
- 'stevearc/oil.nvim':
- Uses single window to show information only about currently explored
directory, while this module uses column view to show whole currently
explored branch.
- Also uses text editing to manipulate file system entries.
- Can work for remote file systems, while this module can not (by design).
- 'nvim-neo-tree/neo-tree.nvim':
- Compares to this module mostly the same as 'nvim-tree/nvim-tree.lua'.
# Highlight groups ~
* `MiniFilesBorder` - border of regular windows.
* `MiniFilesBorderModified` - border of windows showing modified buffer.
* `MiniFilesCursorLine` - cursor line in explorer windows.
* `MiniFilesDirectory` - text and icon representing directory.
* `MiniFilesFile` - text representing file.
* `MiniFilesNormal` - basic foreground/background highlighting.
* `MiniFilesTitle` - title of regular windows.
* `MiniFilesTitleFocused` - title of focused window.
To change any highlight group, modify it directly with |:highlight|.
# Disabling ~
This plugin provides only manually started functionality, so no disabling
is available.
------------------------------------------------------------------------------
*MiniFiles-navigation*
Navigation ~
Every navigation starts by calling |MiniFiles.open()|, either directly or via
mapping (see its help for examples of some common scenarios). It will show
an explorer consisting of side-by-side floating windows with the following
principles:
- Explorer shows one branch of nested directories at a time.
- Explorer consists from several windows:
- Each window displays entries of a single directory in a modifiable
scratch buffer.
- Windows are organized left to right: for any particular window the left
neighbor is its parent directory and right neighbor - its child.
- Explorer windows are the viewport to some part of current branch, meaning
that their opening/closing does not affect the branch. This matters, for
example, if there are more elements in the branch than can be shown windows.
- Every buffer line represents separate file system entry following certain
format (not visible for users by default; set |conceallevel| to 0 to see it)
- Once directory is shown, its buffer is not updated automatically following
external file system changes. Manually use |MiniFiles.synchronize()| for that.
After opening explorer, in-buffer navigation is done the same way as any
regular buffer, except without some keys reserved for built-in actions.
Most common ways to navigate are:
- Press `j` to move cursor onto next (lower) entry in current directory.
- Press `k` to move cursor onto previous (higher) entry in current directory.
- Press `l` to expand entry under cursor (see "Go in" action).
- Press `h` to focus on parent directory (see "Go out" action).
Cursor positions in each directory buffer are tracked and saved during
navigation. This allows for more convenient repeated navigation to some
previously visited branch.
Available built-in actions (see "Details" for more information): >
| Action | Keys | Description |
|-------------|------|------------------------------------------------|
| Close | q | Close explorer |
|-------------|------|------------------------------------------------|
| Go in | l | Expand entry (show directory or open file) |
|-------------|------|------------------------------------------------|
| Go in plus | L | Expand entry plus extra action |
|-------------|------|------------------------------------------------|
| Go out | h | Focus on parent directory |
|-------------|------|------------------------------------------------|
| Go out plus | H | Focus on parent directory plus extra action |
|-------------|------|------------------------------------------------|
| Reset | <BS> | Reset current explorer |
|-------------|------|------------------------------------------------|
| Reveal cwd | @ | Reset current current working directory |
|-------------|------|------------------------------------------------|
| Show help | g? | Show help window |
|-------------|------|------------------------------------------------|
| Synchronize | = | Synchronize user edits and/or external changes |
|-------------|------|------------------------------------------------|
| Trim left | < | Trim left part of branch |
|-------------|------|------------------------------------------------|
| Trim right | > | Trim right part of branch |
|-------------|------|------------------------------------------------|
<
Details:
- "Go in":
- Always opens file in the latest window before `MiniFiles.open()` call.
- Never closes explorer.
- Works in linewise Visual mode to expand multiple entries.
- "Go in plus" is regular "Go in" but closes explorer after opening a file.
- "Go out plus" is regular "Go out" but trims right part of branch.
- "Reset" focuses only on "anchor" directory (the one used to open current
explorer) and resets all stored directory cursor positions.
- "Reveal cwd" extends branch to include |current-directory|.
If it is not an ancestor of the current branch, nothing is done.
- "Show help" results into new window with helpful information about current
explorer. Press `q` to close it.
- "Synchronize" parses user edits in directory buffers, applies them (after
confirmation), and updates all directory buffers with the most relevant
file system information. Can also be used without user edits to show up
to date file system entries.
See |MiniFiles-manipulation| for more info about file system manipulation.
- "Trim left" and "Trim right" trim parts of the whole branch, not only its
currently visible parts.
Notes:
- Each action has exported function with more details about it.
- Keys can be configured with `mappings` table of |MiniFiles.config|.
------------------------------------------------------------------------------
*MiniFiles-manipulation*
Manipulation ~
File system manipulation is done by editing text inside directory buffers,
which are shown inside dedicated window(s). See |MiniFiles-navigation| for
more information about navigating to a particular directory.
General workflow:
- Navigate to the directory in which manipulation should be done.
- Edit buffer in the way representing file system action.
- Repeat previous steps until all necessary file system actions are recorded.
Note: even if directory buffer is hidden, its modifications are preserved,
so you can navigate in and out of directory with modified buffer.
- Execute |MiniFiles.synchronize()| (default key is `=`). This will prompt
confirmation dialog listing all file system actions it is about to perform.
READ IT CAREFULLY.
- Confirm by pressing `y`/`<CR>` (applies edits and updates buffers) or
don't confirm by pressing `n`/`<Esc>` (updates buffers without applying edits).
# How does it work ~
All manipulation functionality is powered by creating and keeping track of
path indexes: text of the form `/xxx` (`xxx` is the number path index) placed
at the start of every line representing file system entry.
By default they are hidden as concealed text (along with prefix separators)
for more convenience but you can see them by setting |conceallevel| to 0.
DO NOT modify text to the left of entry name.
During synchronization, actual text for entry name is compared to path index
at that line (if present) to deduce which file system action to perform.
# Supported file system actions ~
## Create ~
- Create file by creating new line with file name (including extension).
- Create directory by creating new line with directory name followed by `/`.
- Create file or directory inside nested directories by creating new line
with text like 'dir/nested-dir/' or 'dir/nested-dir/file'.
Always use `/` on any OS.
## Delete ~
- Delete file or directory by deleting **whole line** describing it.
- If `options.permanent_delete` is `true`, delete is permanent. Otherwise
file system entry is moved to a module-specific trash directory
(see |MiniFiles.config| for more details).
## Rename ~
- Rename file or directory by editing its name (not icon or path index to
the left of it).
- With default mappings for `h` / `l` it might be not convenient to rename
only part of an entry. You can adopt any of the following approaches:
- Use different motions, like |$|, |e|, |f|, etc.
- Go into Insert mode and navigate inside it.
- Change mappings to be more suited for manipulation and not navigation.
See "Mappings" section in |MiniFiles.config|.
- It is not needed to end directory name with `/`.
- Cyclic renames ("a" to "b" and "b" to "a") are not supported.
## Copy ~
- Copy file or directory by copying **whole line** describing it and pasting
it inside buffer of target directory.
- Change of target path is allowed. Edit only entry name in target location
(not icon or path index to the left of it).
- Copying inside same parent directory is supported only if target path has
different name.
- Copying inside child directory is supported.
## Move ~
- Move file or directory by cutting **whole line** describing it and then
pasting it inside target directory.
- Change of target path is allowed. Edit only entry name in target location
(not icon or path index to the left of it).
- Moving directory inside itself is not supported.
------------------------------------------------------------------------------
*MiniFiles-events*
Events ~
To allow user customization and integration of external tools, certain |User|
autocommand events are triggered under common circumstances.
UI events ~
- `MiniFilesExplorerOpen` - just after explorer finishes opening.
- `MiniFilesExplorerClose` - just before explorer starts closing.
- `MiniFilesBufferCreate` - when buffer is created to show a particular
directory. Triggered once per directory during one explorer session.
Can be used to create buffer-local mappings.
- `MiniFilesBufferUpdate` - when directory buffer is updated with new content.
Can be used for integrations to set |extmarks| with useful information.
- `MiniFilesWindowOpen` - when new window is opened. Can be used to set
window-local settings (like border, 'winblend', etc.)
- `MiniFilesWindowUpdate` - when a window is updated. Triggers frequently,
for example, for every "go in" or "go out" action.
Callback for each UI event will receive `data` field (see |nvim_create_autocmd()|)
with the following information:
- <buf_id> - index of target buffer.
- <win_id> - index of target window. Can be `nil`, like in
`MiniFilesBufferCreate` and buffer's first `MiniFilesBufferUpdate` as
they are triggered before window is created.
File action events ~
- `MiniFilesActionCreate` - after entry is successfully created.
- `MiniFilesActionDelete` - after entry is successfully deleted.
- `MiniFilesActionRename` - after entry is successfully renamed.
- `MiniFilesActionCopy` - after entry is successfully copied.
- `MiniFilesActionMove` - after entry is successfully moved.
Callback for each file action event will receive `data` field
(see |nvim_create_autocmd()|) with the following information:
- <action> - string with action name.
- <from> - absolute path of entry before action (`nil` for "create" action).
- <to> - absolute path of entry after action (`nil` for "delete" action).
------------------------------------------------------------------------------
*MiniFiles-examples*
Common configuration examples ~
# Toggle explorer ~
Use a combination of |MiniFiles.open()| and |MiniFiles.close()|: >lua
local minifiles_toggle = function(...)
if not MiniFiles.close() then MiniFiles.open(...) end
end
<
# Customize windows ~
Create an autocommand for `MiniFilesWindowOpen` event: >lua
vim.api.nvim_create_autocmd('User', {
pattern = 'MiniFilesWindowOpen',
callback = function(args)
local win_id = args.data.win_id
-- Customize window-local settings
vim.wo[win_id].winblend = 50
local config = vim.api.nvim_win_get_config(win_id)
config.border, config.title_pos = 'double', 'right'
vim.api.nvim_win_set_config(win_id, config)
end,
})
<
# Customize icons ~
Use different directory icon: >lua
local my_prefix = function(fs_entry)
if fs_entry.fs_type == 'directory' then
-- NOTE: it is usually a good idea to use icon followed by space
return ' ', 'MiniFilesDirectory'
end
return MiniFiles.default_prefix(fs_entry)
end
require('mini.files').setup({ content = { prefix = my_prefix } })
<
Show no icons: >lua
require('mini.files').setup({ content = { prefix = function() end } })
<
# Create mapping to show/hide dot-files ~
Create an autocommand for `MiniFilesBufferCreate` event which calls
|MiniFiles.refresh()| with explicit `content.filter` functions: >lua
local show_dotfiles = true
local filter_show = function(fs_entry) return true end
local filter_hide = function(fs_entry)
return not vim.startswith(fs_entry.name, '.')
end
local toggle_dotfiles = function()
show_dotfiles = not show_dotfiles
local new_filter = show_dotfiles and filter_show or filter_hide
MiniFiles.refresh({ content = { filter = new_filter } })
end
vim.api.nvim_create_autocmd('User', {
pattern = 'MiniFilesBufferCreate',
callback = function(args)
local buf_id = args.data.buf_id
-- Tweak left-hand side of mapping to your liking
vim.keymap.set('n', 'g.', toggle_dotfiles, { buffer = buf_id })
end,
})
<
# Create mappings to modify target window via split ~
Combine |MiniFiles.get_target_window()| and |MiniFiles.set_target_window()|: >lua
local map_split = function(buf_id, lhs, direction)
local rhs = function()
-- Make new window and set it as target
local new_target_window
vim.api.nvim_win_call(MiniFiles.get_target_window(), function()
vim.cmd(direction .. ' split')
new_target_window = vim.api.nvim_get_current_win()
end)
MiniFiles.set_target_window(new_target_window)
end
-- Adding `desc` will result into `show_help` entries
local desc = 'Split ' .. direction
vim.keymap.set('n', lhs, rhs, { buffer = buf_id, desc = desc })
end
vim.api.nvim_create_autocmd('User', {
pattern = 'MiniFilesBufferCreate',
callback = function(args)
local buf_id = args.data.buf_id
-- Tweak keys to your liking
map_split(buf_id, 'gs', 'belowright horizontal')
map_split(buf_id, 'gv', 'belowright vertical')
end,
})
<
# Create mapping to set current working directory ~
Use |MiniFiles.get_fs_entry()| together with |vim.fs.dirname()|: >lua
local files_set_cwd = function(path)
-- Works only if cursor is on the valid file system entry
local cur_entry_path = MiniFiles.get_fs_entry().path
local cur_directory = vim.fs.dirname(cur_entry_path)
vim.fn.chdir(cur_directory)
end
vim.api.nvim_create_autocmd('User', {
pattern = 'MiniFilesBufferCreate',
callback = function(args)
vim.keymap.set('n', 'g~', files_set_cwd, { buffer = args.data.buf_id })
end,
})
<
------------------------------------------------------------------------------
*MiniFiles.setup()*
`MiniFiles.setup`({config})
Module setup
Parameters ~
{config} `(table|nil)` Module config table. See |MiniFiles.config|.
Usage ~
>lua
require('mini.files').setup() -- use default config
-- OR
require('mini.files').setup({}) -- replace {} with your config table
<
------------------------------------------------------------------------------
*MiniFiles.config*
`MiniFiles.config`
Module config
Default values:
>lua
MiniFiles.config = {
-- Customization of shown content
content = {
-- Predicate for which file system entries to show
filter = nil,
-- What prefix to show to the left of file system entry
prefix = nil,
-- In which order to show file system entries
sort = nil,
},
-- Module mappings created only inside explorer.
-- Use `''` (empty string) to not create one.
mappings = {
close = 'q',
go_in = 'l',
go_in_plus = 'L',
go_out = 'h',
go_out_plus = 'H',
reset = '<BS>',
reveal_cwd = '@',
show_help = 'g?',
synchronize = '=',
trim_left = '<',
trim_right = '>',
},
-- General options
options = {
-- Whether to delete permanently or move into module-specific trash
permanent_delete = true,
-- Whether to use for editing directories
use_as_default_explorer = true,
},
-- Customization of explorer windows
windows = {
-- Maximum number of windows to show side by side
max_number = math.huge,
-- Whether to show preview of file/directory under cursor
preview = false,
-- Width of focused window
width_focus = 50,
-- Width of non-focused window
width_nofocus = 15,
-- Width of preview window
width_preview = 25,
},
}
<
# Content ~
`content.filter` is a predicate which takes file system entry data as input
and returns `true`-ish value if it should be shown.
Uses |MiniFiles.default_filter()| by default.
A file system entry data is a table with the following fields:
- <fs_type> `(string)` - one of "file" or "directory".
- <name> `(string)` - basename of an entry (including extension).
- <path> `(string)` - full path of an entry.
`content.prefix` describes what text (prefix) to show to the left of file
system entry name (if any) and how to highlight it. It also takes file
system entry data as input and returns tuple of text and highlight group
name to be used to highlight prefix. See |MiniFiles-examples| for common
examples of how to use it.
Note: due to how lines are parsed to detect user edits for file system
manipulation, output of `content.prefix` should not contain `/` character.
Uses |MiniFiles.default_prefix()| by default.
`content.sort` describes in which order directory entries should be shown
in directory buffer. Takes as input and returns as output an array of file
system entry data. Note: technically, it can be used to filter and modify
its elements as well.
Uses |MiniFiles.default_sort()| by default.
# Mappings ~
`mappings` table can be used to customize buffer-local mappings created in each
directory buffer for built-in actions. Entry name corresponds to the function
name of the action, value - right hand side of the mapping. Supply empty
string to not create a particular mapping.
Default mappings are mostly designed for consistent navigation experience.
Here are some alternatives: >lua
-- Close explorer after opening file with `l`
mappings = {
go_in = 'L',
go_in_plus = 'l',
}
-- Don't use `h`/`l` for easier cursor navigation during text edit
mappings = {
go_in = 'L',
go_in_plus = '',
go_out = 'H',
go_out_plus = '',
}
<
# Options ~
`options.use_as_default_explorer` is a boolean indicating whether this module
should be used as a default file explorer for editing directories (instead of
|netrw| by default).
`options.permanent_delete` is a boolean indicating whether to perform
permanent delete or move into special trash directory.
This is a module-specific variant of "remove to trash".
Target directory is 'mini.files/trash' inside standard path of Neovim data
directory (execute `:echo stdpath('data')` to see its path in your case).
# Windows ~
`windows.max_number` is a maximum number of windows allowed to be open
simultaneously. For example, use value 1 to always show single window.
There is no constraint by default.
`windows.preview` is a boolean indicating whether to show preview of
file/directory under cursor. Note: it is shown with highlighting if Neovim
version is sufficient and file is small enough (less than 1K bytes per line
or 1M bytes in total).
`windows.width_focus` and `windows.width_nofocus` are number of columns used
as `width` for focused and non-focused windows respectively.
------------------------------------------------------------------------------
*MiniFiles.open()*
`MiniFiles.open`({path}, {use_latest}, {opts})
Open file explorer
Common ways to use this function: >lua
-- Open current working directory in a last used state
MiniFiles.open()
-- Fresh explorer in current working directory
MiniFiles.open(nil, false)
-- Open directory of current file (in last used state) focused on the file
MiniFiles.open(vim.api.nvim_buf_get_name(0))
-- Fresh explorer in directory of current file
MiniFiles.open(vim.api.nvim_buf_get_name(0), false)
-- Open last used `path` (per tabpage)
-- Current working directory for the first time
MiniFiles.open(MiniFiles.get_latest_path())
<
Parameters ~
{path} `(string|nil)` A valid file system path used as anchor.
If it is a path to directory, used directly.
If it is a path to file, its parent directory is used as anchor while
explorer will focus on the supplied file.
Default: path of |current-directory|.
{use_latest} `(boolean|nil)` Whether to load explorer state from history
(based on the supplied anchor path). Default: `true`.
{opts} `(table|nil)` Table of options overriding |MiniFiles.config| and
`vim.b.minifiles_config` for this particular explorer session.
------------------------------------------------------------------------------
*MiniFiles.refresh()*
`MiniFiles.refresh`({opts})
Refresh explorer
Notes:
- If in `opts` at least one of `content` entry is not `nil`, all directory
buffers are forced to update.
Parameters ~
{opts} `(table|nil)` Table of options to update.
------------------------------------------------------------------------------
*MiniFiles.synchronize()*
`MiniFiles.synchronize`()
Synchronize explorer
- Parse user edits in directory buffers.
- Convert edits to file system actions and apply them after confirmation.
- Update all directory buffers with the most relevant file system information.
Can be used without user edits to account for external file system changes.
------------------------------------------------------------------------------
*MiniFiles.reset()*
`MiniFiles.reset`()
Reset explorer
- Show single window focused on anchor directory (which was used as first
argument for |MiniFiles.open()|).
- Reset all tracked directory cursors to point at first entry.
------------------------------------------------------------------------------
*MiniFiles.close()*
`MiniFiles.close`()
Close explorer
Return ~
`(boolean|nil)` Whether closing was done or `nil` if there was nothing to close.
------------------------------------------------------------------------------
*MiniFiles.go_in()*
`MiniFiles.go_in`({opts})
Go in entry under cursor
Depends on entry under cursor:
- If directory, focus on it in the window to the right.
- If file, open it in the window which was current during |MiniFiles.open()|.
Explorer is not closed after that.
Parameters ~
{opts} Options. Possible fields:
- <close_on_file> `(boolean)` - whether to close explorer after going
inside a file. Powers the `go_in_plus` mapping.
Default: `false`.
------------------------------------------------------------------------------
*MiniFiles.go_out()*
`MiniFiles.go_out`()
Go out to parent directory
- Focus on window to the left showing parent of current directory.
------------------------------------------------------------------------------
*MiniFiles.trim_left()*
`MiniFiles.trim_left`()
Trim left part of branch
- Remove all branch paths to the left of currently focused one. This also
results into current window becoming the most left one.
------------------------------------------------------------------------------
*MiniFiles.trim_right()*
`MiniFiles.trim_right`()
Trim right part of branch
- Remove all branch paths to the right of currently focused one. This also
results into current window becoming the most right one.
------------------------------------------------------------------------------
*MiniFiles.reveal_cwd()*
`MiniFiles.reveal_cwd`()
Reveal current working directory
- Prepend branch with parent paths until current working directory is reached.
Do nothing if not inside it.
------------------------------------------------------------------------------
*MiniFiles.show_help()*
`MiniFiles.show_help`()
Show help window
- Open window with helpful information about currently shown explorer and
focus on it. To close it, press `q`.
------------------------------------------------------------------------------
*MiniFiles.get_fs_entry()*
`MiniFiles.get_fs_entry`({buf_id}, {line})
Get file system entry data
Parameters ~
{buf_id} `(number|nil)` Buffer identifier of valid directory buffer.
Default: current buffer.
{line} `(number|nil)` Line number of entry for which to return information.
Default: cursor line.
Return ~
`(table|nil)` Table of file system entry data with the following fields:
- <fs_type> `(string)` - one of "file" or "directory".
- <name> `(string)` - basename of an entry (including extension).
- <path> `(string)` - full path of an entry.
Returns `nil` if there is no proper file system entry path at the line.
------------------------------------------------------------------------------
*MiniFiles.get_target_window()*
`MiniFiles.get_target_window`()
Get target window
Return ~
`(number|nil)` Window identifier inside which file will be opened or
`nil` if no explorer is opened.
------------------------------------------------------------------------------
*MiniFiles.set_target_window()*
`MiniFiles.set_target_window`({win_id})
Set target window
Parameters ~
{win_id} `(number)` Window identifier inside which file will be opened.
------------------------------------------------------------------------------
*MiniFiles.get_latest_path()*
`MiniFiles.get_latest_path`()
Get latest used anchor path
Note: if latest used `path` argument for |MiniFiles.open()| was for file,
this will return its parent (as it was used as anchor path).
------------------------------------------------------------------------------
*MiniFiles.default_filter()*
`MiniFiles.default_filter`({fs_entry})
Default filter of file system entries
Currently does not filter anything out.
Parameters ~
{fs_entry} `(table)` Table with the following fields:
- <fs_type> `(string)` - one of "file" or "directory".
- <name> `(string)` - basename of an entry (including extension).
- <path> `(string)` - full path of an entry.
Return ~
`(boolean)` Always `true`.
------------------------------------------------------------------------------
*MiniFiles.default_prefix()*
`MiniFiles.default_prefix`({fs_entry})
Default prefix of file system entries
- If |MiniIcons| is set up, use |MiniIcons.get()| for "directory"/"file" category.
- Otherwise:
- For directory return fixed icon and "MiniFilesDirectory" group name.
- For file try to use `get_icon()` from 'nvim-tree/nvim-web-devicons'.
If missing, return fixed icon and 'MiniFilesFile' group name.
Parameters ~
{fs_entry} `(table)` Table with the following fields:
- <fs_type> `(string)` - one of "file" or "directory".
- <name> `(string)` - basename of an entry (including extension).
- <path> `(string)` - full path of an entry.
Return ~
`(...)` Icon and highlight group name. For more details, see |MiniFiles.config|
and |MiniFiles-examples|.
------------------------------------------------------------------------------
*MiniFiles.default_sort()*
`MiniFiles.default_sort`({fs_entries})
Default sort of file system entries
Sort directories and files separately (alphabetically ignoring case) and
put directories first.
Parameters ~
{fs_entries} `(table)` Array of file system entry data.
Each one is a table with the following fields:
- <fs_type> `(string)` - one of "file" or "directory".
- <name> `(string)` - basename of an entry (including extension).
- <path> `(string)` - full path of an entry.
Return ~
`(table)` Sorted array of file system entries.
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -0,0 +1,160 @@
*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.
------------------------------------------------------------------------------
*MiniFuzzy-algorithm*
# 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`.
------------------------------------------------------------------------------
*MiniFuzzy.setup()*
`MiniFuzzy.setup`({config})
Module setup
Parameters ~
{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.config*
`MiniFuzzy.config`
Module config
Default values:
>lua
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,
}
<
------------------------------------------------------------------------------
*MiniFuzzy.match()*
`MiniFuzzy.match`({word}, {candidate})
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.
Parameters ~
{word} `(string)` Input word (usually user input).
{candidate} `(string)` Target word (usually with which matching is done).
Return ~
`(table)` Table with matching information (see function's description).
------------------------------------------------------------------------------
*MiniFuzzy.filtersort()*
`MiniFuzzy.filtersort`({word}, {candidate_array})
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).
Parameters ~
{word} `(string)` String which will be searched.
{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.process_lsp_items()*
`MiniFuzzy.process_lsp_items`({items}, {base})
Fuzzy matching for `lsp_completion.process_items` of |MiniCompletion.config|
Parameters ~
{items} `(table)` Array with LSP 'textDocument/completion' response items.
{base} `(string)` Word to complete.
------------------------------------------------------------------------------
*MiniFuzzy.get_telescope_sorter()*
`MiniFuzzy.get_telescope_sorter`({opts})
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.
Parameters ~
{opts} `(table|nil)` Options (currently not used).
Usage ~
>lua
require('telescope').setup({
defaults = {
generic_sorter = require('mini.fuzzy').get_telescope_sorter
}
})
<
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -0,0 +1,426 @@
*mini.git* Git integration
*MiniGit*
MIT License Copyright (c) 2024 Evgeni Chasnovski
==============================================================================
Features:
- Automated tracking of Git related data: root path, status, HEAD, etc.
Exposes buffer-local variables for convenient use in statusline.
See |MiniGit.enable()| and |MiniGit.get_buf_data()| for more information.
- |:Git| command for executing any `git` call inside file's repository root with
deeper current instance integration (show output as notification/buffer,
use to edit commit messages, etc.).
- Helper functions to inspect Git history:
- |MiniGit.show_range_history()| shows how certain line range evolved.
- |MiniGit.show_diff_source()| shows file state as it was at diff entry.
- |MiniGit.show_at_cursor()| shows Git related data depending on context.
What it doesn't do:
- Replace fully featured Git client. Rule of thumb: if feature does not rely
on a state of current Neovim (opened buffers, etc.), it is out of scope.
For more functionality, use either |MiniDiff| or fully featured Git client.
Sources with more details:
- |:Git|
- |MiniGit-examples|
- |MiniGit.enable()|
- |MiniGit.get_buf_data()|
# Setup ~
This module needs a setup with `require('mini.git').setup({})` (replace `{}` with
your `config` table). It will create global Lua table `MiniGit` which you can use
for scripting or manually (with `:lua MiniGit.*`).
See |MiniGit.config| for `config` structure and default values.
# Comparisons ~
- 'tpope/vim-fugitive':
- Mostly a dedicated Git client, while this module is not (by design).
- Provides buffer-local Git data only through fixed statusline component,
while this module has richer data in the form of a Lua table.
- Both provide |:Git| command with 'vim-fugitive' treating some cases
extra specially (like `:Git blame`, etc.), while this module mostly
treats all cases the same. See |MiniGit-examples| for how they can be
manually customized.
Also this module provides slightly different (usually richer)
completion suggestions.
- 'NeogitOrg/neogit':
- Similar to 'tpope/vim-fugitive', but without `:Git` command.
- 'lewis6991/gitsigns.nvim':
- Provides buffer-local Git data with emphasis on granular diff status,
while this module is more oriented towards repository and file level
data (root, HEAD, file status, etc.). Use |MiniDiff| for diff tracking.
# Disabling ~
To prevent buffer(s) from being tracked, set `vim.g.minigit_disable` (globally)
or `vim.b.minigit_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.
------------------------------------------------------------------------------
*MiniGit-examples*
# Statusline component ~
Tracked buffer data can be used in statusline via `vim.b.minigit_summary_string`
buffer-local variable. It is expected to be used as is. To show another info,
tweak buffer-local variable directly inside `MiniGitUpdated` `User` event: >lua
-- Use only HEAD name as summary string
local format_summary = function(data)
-- Utilize buffer-local table summary
local summary = vim.b[data.buf].minigit_summary
vim.b[data.buf].minigit_summary_string = summary.head_name or ''
end
local au_opts = { pattern = 'MiniGitUpdated', callback = format_summary }
vim.api.nvim_create_autocmd('User', au_opts)
<
# Tweaking command output ~
Buffer output of |:Git| command can be tweaked inside autocommand for
`MiniGitCommandSplit` `User` event (see |MiniGit-command-events|).
For example, to make `:vertical Git blame -- %` align blame output with the
current window state, use the following code: >lua
local align_blame = function(au_data)
if au_data.data.git_subcommand ~= 'blame' then return end
-- Align blame output with source
local win_src = au_data.data.win_source
vim.wo.wrap = false
vim.fn.winrestview({ topline = vim.fn.line('w0', win_src) })
vim.api.nvim_win_set_cursor(0, { vim.fn.line('.', win_src), 0 })
-- Bind both windows so that they scroll together
vim.wo[win_src].scrollbind, vim.wo.scrollbind = true, true
end
local au_opts = { pattern = 'MiniGitCommandSplit', callback = align_blame }
vim.api.nvim_create_autocmd('User', au_opts)
<
# History navigation ~
Function |MiniGit.show_at_cursor()| is specifically exported to make Git
history navigation easier. Here are some different ways it can be used:
- Call inside buffer for already committed file to show the evolution of
the current line (or visually selected range) through history.
It is essentially a `:Git log HEAD` with proper `-L` flag.
This also works inside output of |MiniGit.show_diff_source()|.
- Call with cursor on commit hash to inspect that commit in full.
This is usually helpful in the output of `:Git log`.
- Call with cursor inside diff entry to inspect its file in the state how it
was at certain commit. By default it shows state after commit, unless cursor
is on the "deleted" line (i.e. line starting with "-") in which case
state before commit is shown.
This workflow can be made more interactive when used with mapping, like this: >lua
local rhs = '<Cmd>lua MiniGit.show_at_cursor()<CR>'
vim.keymap.set({ 'n', 'x' }, '<Leader>gs', rhs, { desc = 'Show at cursor' })
<
------------------------------------------------------------------------------
*MiniGit-command*
*:Git*
The `:Git` user command runs `git` CLI call with extra integration for currently
opened Neovim process:
- Command is executed inside repository root of the currently active file
(or |current-directory| if file is not tracked by this module).
- Command output is shown either in dedicated buffer in window split or as
notification via |vim.notify()|. Which method is used depends on whether
particular Git subcommand is supposed to show data for user to inspect
(like `log`, `status`, etc.) or not (like `commit`, `push`, etc.). This is
determined automatically based on the data Git itself provides.
Split window is made current after command execution.
Use split-related |command-modifiers| (|:vertical|, |:horizontal|, or |:tab|)
to force output in a particular type of split. Default split direction is
controlled by `command.split` in |MiniGit.config|.
Use |:silent| command modifier to not show any output.
Errors and warnings are always shown as notifications.
See |MiniGit-examples| for the example of tweaking command output.
- Editor for tasks that require interactive user input (like `:Git commit` or
`:Git rebase --interactive`) is opened inside current session in a separate
split. Make modifications as in regular buffer, |:write| changes followed by
|:close| / |:quit| for Git CLI command to resume.
Examples of usage:
- `:Git log --oneline` - show compact log of current repository.
- `:vert Git blame -- %` - show latest commits per line in vertical split.
- `:Git help rebase` - show help page for `rebase` subcommand.
- `:Git -C <cwd> status` - execute `git status` inside |current-directory|.
There is also a context aware completion which can be invoked with `<Tab>`:
- If completed word starts with "-", options for the current Git subcommand
are shown. Like completion at `:Git log -` will suggest `-L`, `--oneline`, etc.
- If there is an explicit " -- " to the cursor's left, incremental path
suggestions will be shown.
- If there is no recognized Git subcommand yet, show list of subcommands.
Otherwise for some common subcommands list of its targets will be suggested:
like for `:Git branch` it will be list of branches, etc.
Notes:
- Paths are always treated as relative to command's execution directory
(file's repository root or |current-directory| if absent).
- Don't use quotes for entries containing space, escape it with `\` directly.
Like `:Git commit -m Hello\ world` and not `:Git commit -m 'Hello world'`
(which treats `'Hello` and `world'` as separate arguments).
*MiniGit-command-events*
There are several `User` events triggered during command execution:
- `MiniGitCommandDone` - after command is done executing. For Lua callbacks it
provides a special `data` table with the following fields:
- <cmd_input> `(table)` - structured data about executed command.
Has same structure as Lua function input in |nvim_create_user_command()|.
- <cwd> `(string)` - directory path inside which Git command was executed.
- `<exit_code>` `(number)` - exit code of CLI process.
- `<git_command>` `(table)` - array with arguments of full executed command.
- `<git_subcommand>` `(string)` - detected Git subcommand (like "log", etc.).
- `<stderr>` `(string)` - `stderr` process output.
- `<stdout>` `(string)` - `stdout` process output.
- `MiniGitCommandSplit` - after command showed its output in a split. Triggered
after `MiniGitCommandDone` and provides similar `data` table with extra fields:
- `<win_source>` `(number)` - window identifier of "source" window (current at
the moment before command execution).
- `<win_stdout>` `(number)` - window identifier of command output.
------------------------------------------------------------------------------
*MiniGit.setup()*
`MiniGit.setup`({config})
Module setup
Besides general side effects (see |mini.nvim|), it also:
- Sets up auto enabling in every normal buffer for an actual file on disk.
- Creates |:Git| command.
Parameters ~
{config} `(table|nil)` Module config table. See |MiniGit.config|.
Usage ~
>lua
require('mini.git').setup() -- use default config
-- OR
require('mini.git').setup({}) -- replace {} with your config table
<
------------------------------------------------------------------------------
*MiniGit.config*
`MiniGit.config`
Module config
Default values:
>lua
MiniGit.config = {
-- General CLI execution
job = {
-- Path to Git executable
git_executable = 'git',
-- Timeout (in ms) for each job before force quit
timeout = 30000,
},
-- Options for `:Git` command
command = {
-- Default split direction
split = 'auto',
},
}
<
# Job ~
`config.job` contains options for customizing CLI executions.
`job.git_executable` defines a full path to Git executable. Default: "git".
`job.timeout` is a duration (in ms) from job start until it is forced to stop.
Default: 30000.
# Command ~
`config.command` contains options for customizing |:Git| command.
`command.split` defines default split direction for |:Git| command output. Can be
one of "horizontal", "vertical", "tab", or "auto". Value "auto" uses |:vertical|
if only 'mini.git' buffers are shown in the tabpage and |:tab| otherwise.
Default: "auto".
------------------------------------------------------------------------------
*MiniGit.show_at_cursor()*
`MiniGit.show_at_cursor`({opts})
Show Git related data at cursor
- If there is a commit-like |<cword>|, show it in split with `git show`.
- If possible, show diff source via |MiniGit.show_diff_source()|.
- If possible, show range history via |MiniGit.show_range_history()|.
- Otherwise throw an error.
Parameters ~
{opts} `(table|nil)` Options. Possible values:
- <split> `(string)` - split direction. One of "horizontal", "vertical",
"tab", or "auto" (default). Value "auto" uses |:vertical| if only 'mini.git'
buffers are shown in the tabpage and |:tab| otherwise.
- Fields appropriate for forwarding to other functions.
------------------------------------------------------------------------------
*MiniGit.show_diff_source()*
`MiniGit.show_diff_source`({opts})
Show diff source
When buffer contains text formatted as unified patch (like after
`:Git log --patch`, `:Git diff`, or |MiniGit.show_range_history()|),
show state of the file at the particular state. Target commit/state, path,
and line number are deduced from cursor position.
Notes:
- Needs |current-directory| to be the Git root for relative paths to work.
- Needs cursor to be inside hunk lines or on "---" / "+++" lines with paths.
- Only basic forms of `:Git diff` output is supported: `:Git diff`,
`:Git diff --cached`, and `:Git diff <commit>`.
Parameters ~
{opts} `(table|nil)` Options. Possible values:
- <split> `(string)` - split direction. One of "horizontal", "vertical",
"tab", or "auto" (default). Value "auto" uses |:vertical| if only 'mini.git'
buffers are shown in the tabpage and |:tab| otherwise.
- <target> `(string)` - which file state to show. One of "before", "after",
"both" (both states in vertical split), "auto" (default). Value "auto"
shows "before" state if cursor line starts with "-", otherwise - "after".
------------------------------------------------------------------------------
*MiniGit.show_range_history()*
`MiniGit.show_range_history`({opts})
Show range history
Compute and show in split data about how particular line range in current
buffer evolved through Git history. Essentially a `git log` with `-L` flag.
Notes:
- Works well with |MiniGit.diff_foldexpr()|.
- Does not work if there are uncommited changes, as there is no easy way to
compute effective range line numbers.
Parameters ~
{opts} `(table|nil)` Options. Possible fields:
- <line_start> `(number)` - range start line.
- <line_end> `(number)` - range end line.
If both <line_start> and <line_end> are not supplied, they default to
current line in Normal mode and visual selection in Visual mode.
- <log_args> `(table)` - array of options to append to `git log` call.
- <split> `(string)` - split direction. One of "horizontal", "vertical",
"tab", or "auto" (default). Value "auto" uses |:vertical| if only 'mini.git'
buffers are shown in the tabpage and |:tab| otherwise.
------------------------------------------------------------------------------
*MiniGit.diff_foldexpr()*
`MiniGit.diff_foldexpr`({lnum})
Fold expression for Git logs
Folds contents of hunks, file patches, and log entries in unified diff.
Useful for filetypes "diff" (like after `:Git diff`) and "git" (like after
`:Git log --patch` or `:Git show` for commit).
Works well with |MiniGit.show_range_history()|.
General idea of folding levels (use |zr| and |zm| to adjust interactively):
- At level 0 there is one line per whole patch or log entry.
- At level 1 there is one line per patched file.
- At level 2 there is one line per hunk.
- At level 3 there is no folds.
For automated setup, set the following for "git" and "diff" filetypes (either
inside |FileType| autocommand or |ftplugin|): >vim
setlocal foldmethod=expr foldexpr=v:lua.MiniGit.diff_foldexpr()
<
Parameters ~
{lnum} `(number|nil)` Line number for which fold level is computed.
Default: |v:lnum|.
Return ~
`(number|string)` Line fold level. See |fold-expr|.
------------------------------------------------------------------------------
*MiniGit.enable()*
`MiniGit.enable`({buf_id})
Enable Git tracking in a file buffer
Tracking is done by reacting to changes in file content or file's repository
in the form of keeping buffer data up to date. The data can be used via:
- |MiniGit.get_buf_data()|. See its help for a list of actually tracked data.
- `vim.b.minigit_summary` (table) and `vim.b.minigit_summary_string` (string)
buffer-local variables which are more suitable for statusline.
`vim.b.minigit_summary_string` contains information about HEAD, file status,
and in progress action (see |MiniGit.get_buf_data()| for more details).
See |MiniGit-examples| for how it can be tweaked and used in statusline.
Note: this function is called automatically for all new normal buffers.
Use it explicitly if buffer was disabled.
`User` event `MiniGitUpdated` is triggered whenever tracking data is updated.
Note that not all data listed in |MiniGit.get_buf_data()| can be present (yet)
at the point of event being triggered.
Parameters ~
{buf_id} `(number)` Target buffer identifier. Default: 0 for current buffer.
------------------------------------------------------------------------------
*MiniGit.disable()*
`MiniGit.disable`({buf_id})
Disable Git tracking in buffer
Parameters ~
{buf_id} `(number)` Target buffer identifier. Default: 0 for current buffer.
------------------------------------------------------------------------------
*MiniGit.toggle()*
`MiniGit.toggle`({buf_id})
Toggle Git tracking in buffer
Enable if disabled, disable if enabled.
Parameters ~
{buf_id} `(number)` Target buffer identifier. Default: 0 for current buffer.
------------------------------------------------------------------------------
*MiniGit.get_buf_data()*
`MiniGit.get_buf_data`({buf_id})
Get buffer data
Parameters ~
{buf_id} `(number)` Target buffer identifier. Default: 0 for current buffer.
Return ~
`(table|nil)` Table with buffer Git data or `nil` if buffer is not enabled.
If the file is not part of Git repo, table will be empty.
Table has the following fields:
- <repo> `(string)` - full path to '.git' directory.
- <root> `(string)` - full path to worktree root.
- <head> `(string)` - full commit of current HEAD.
- <head_name> `(string)` - short name of current HEAD (like "master").
For detached HEAD it is "HEAD".
- <status> `(string)` - two character file status as returned by `git status`.
- <in_progress> `(string)` - name of action(s) currently in progress
(bisect, merge, etc.). Can be a combination of those separated by ",".
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -0,0 +1,487 @@
*mini.hipatterns* Highlight patterns in text
*MiniHipatterns*
MIT License Copyright (c) 2023 Evgeni Chasnovski
==============================================================================
Features:
- Highlight text with configurable patterns and highlight groups (can be
string or callable).
- Highlighting is updated asynchronously with configurable debounce delay.
- Function to get matches in a buffer (see |MiniHipatterns.get_matches()|).
See |MiniHipatterns-examples| for common configuration examples.
Notes:
- It does not define any highlighters by default. Add to `config.highlighters`
to have a visible effect.
- Sometimes (especially during frequent buffer updates on same line numbers)
highlighting can be outdated or not applied when it should be. This is due
to asynchronous nature of updates reacting to text changes (via
`on_lines` of |nvim_buf_attach()|).
To make them up to date, use one of the following:
- Scroll window (for example, with |CTRL-E| / |CTRL-Y|). This will ensure
up to date highlighting inside window view.
- Hide and show buffer.
- Execute `:edit` (if you enabled highlighting with |MiniHipatterns.setup()|).
- Manually call |MiniHipatterns.update()|.
- If you experience flicker when typing near highlighted pattern in Insert
mode, it might be due to `delay` configuration of 'mini.completion' or
using built-in completion.
For better experience with 'mini.completion', make sure that its
`delay.completion` is less than this module's `delay.text_change` (which
it is by default).
The reason for this is (currently unresolvable) limitations of Neovim's
built-in completion implementation.
# Setup ~
Setting up highlights can be done in two ways:
- Manually for every buffer with `require('mini.hipatterns').enable()`.
This will enable highlighting only in one particular buffer until it is
unloaded (which also includes calling `:edit` on current file).
- Globally with `require('mini.hipatterns').setup({})` (replace `{}` with
your `config` table). This will auto-enable highlighting in "normal"
buffers (see 'buftype'). Use |MiniHipatterns.enable()| to manually enable
in other buffers.
It will also create global Lua table `MiniHipatterns` which you can use
for scripting or manually (with `:lua MiniHipatterns.*`).
See |MiniHipatterns.config| for `config` structure and default values.
You can override runtime config settings (like highlighters and delays)
locally to buffer inside `vim.b.minihipatterns_config` which should have
same structure as `MiniHipatterns.config`.
See |mini.nvim-buffer-local-config| for more details.
# Comparisons ~
- 'folke/todo-comments':
- Oriented for "TODO", "NOTE", "FIXME" like patterns, while this module
can work with any Lua patterns and computable highlight groups.
- Has functionality beyond text highlighting (sign placing,
"telescope.nvim" extension, etc.), while this module only focuses on
highlighting text.
- 'folke/paint.nvim':
- Mostly similar to this module, but with slightly less functionality,
such as computed pattern and highlight group, asynchronous delay, etc.
- 'NvChad/nvim-colorizer.lua':
- Oriented for color highlighting, while this module can work with any
Lua patterns and computable highlight groups.
- Has more built-in color spaces to highlight, while this module out of
the box provides only hex color highlighting
(see |MiniHipatterns.gen_highlighter.hex_color()|). Other types are
also possible to implement.
- 'uga-rosa/ccc.nvim':
- Has more than color highlighting functionality, which is compared to
this module in the same way as 'NvChad/nvim-colorizer.lua'.
# Highlight groups ~
* `MiniHipatternsFixme` - suggested group to use for `FIXME`-like patterns.
* `MiniHipatternsHack` - suggested group to use for `HACK`-like patterns.
* `MiniHipatternsTodo` - suggested group to use for `TODO`-like patterns.
* `MiniHipatternsNote` - suggested group to use for `NOTE`-like patterns.
To change any highlight group, modify it directly with |:highlight|.
# Disabling ~
This module can be disabled in three ways:
- Globally: set `vim.g.minihipatterns_disable` to `true`.
- Locally for buffer permanently: set `vim.b.minihipatterns_disable` to `true`.
- Locally for buffer temporarily (until next auto-enabling event if set up
with |MiniHipatterns.setup()|): call |MiniHipatterns.disable()|.
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.
------------------------------------------------------------------------------
*MiniHipatterns-examples*
# Common configuration examples ~
- Special words used to convey different level of attention: >lua
require('mini.hipatterns').setup({
highlighters = {
fixme = { pattern = 'FIXME', group = 'MiniHipatternsFixme' },
hack = { pattern = 'HACK', group = 'MiniHipatternsHack' },
todo = { pattern = 'TODO', group = 'MiniHipatternsTodo' },
note = { pattern = 'NOTE', group = 'MiniHipatternsNote' },
}
})
<
- To match only when pattern appears as a standalone word, use frontier
patterns `%f`. For example, instead of `'TODO'` pattern use
`'%f[%w]()TODO()%f[%W]'`. In this case, for example, 'TODOING' or 'MYTODO'
won't match, but 'TODO' and 'TODO:' will.
- Color hex (like `#rrggbb`) highlighting: >lua
local hipatterns = require('mini.hipatterns')
hipatterns.setup({
highlighters = {
hex_color = hipatterns.gen_highlighter.hex_color(),
}
})
<
You can customize which part of hex color is highlighted by using `style`
field of input options. See |MiniHipatterns.gen_highlighter.hex_color()|.
- Colored words: >lua
local words = { red = '#ff0000', green = '#00ff00', blue = '#0000ff' }
local word_color_group = function(_, match)
local hex = words[match]
if hex == nil then return nil end
return MiniHipatterns.compute_hex_color_group(hex, 'bg')
end
local hipatterns = require('mini.hipatterns')
hipatterns.setup({
highlighters = {
word_color = { pattern = '%S+', group = word_color_group },
},
})
<
- Trailing whitespace (if don't want to use more specific 'mini.trailspace'): >lua
{ pattern = '%f[%s]%s*$', group = 'Error' }
<
- Censor certain sensitive information: >lua
local censor_extmark_opts = function(_, match, _)
local mask = string.rep('x', vim.fn.strchars(match))
return {
virt_text = { { mask, 'Comment' } }, virt_text_pos = 'overlay',
priority = 200, right_gravity = false,
}
end
require('mini.hipatterns').setup({
highlighters = {
censor = {
pattern = 'password: ()%S+()',
group = '',
extmark_opts = censor_extmark_opts,
},
},
})
<
- Enable only in certain filetypes. There are at least these ways to do it:
- (Suggested) With `vim.b.minihipatterns_config` in |filetype-plugin|.
Basically, create "after/ftplugin/<filetype>.lua" file in your config
directory (see |$XDG_CONFIG_HOME|) and define `vim.b.minihipatterns_config`
there with filetype specific highlighters.
This assumes `require('mini.hipatterns').setup()` call.
For example, to highlight keywords in EmmyLua comments in Lua files,
create "after/ftplugin/lua.lua" with the following content: >lua
vim.b.minihipatterns_config = {
highlighters = {
emmylua = { pattern = '^%s*%-%-%-()@%w+()', group = 'Special' }
}
}
<
- Use callable `pattern` with condition. For example: >lua
require('mini.hipatterns').setup({
highlighters = {
emmylua = {
pattern = function(buf_id)
if vim.bo[buf_id].filetype ~= 'lua' then return nil end
return '^%s*%-%-%-()@%w+()'
end,
group = 'Special',
},
},
})
<
- Disable only in certain filetypes. Enable with |MiniHipatterns.setup()|
and set `vim.b.minihipatterns_disable` buffer-local variable to `true` for
buffer you want disabled. See |mini.nvim-disabling-recipes| for more examples.
------------------------------------------------------------------------------
*MiniHipatterns.setup()*
`MiniHipatterns.setup`({config})
Module setup
Parameters ~
{config} `(table|nil)` Module config table. See |MiniHipatterns.config|.
Usage ~
>lua
require('mini.hipatterns').setup({}) -- replace {} with your config table
-- needs `highlighters` field present
<
------------------------------------------------------------------------------
*MiniHipatterns.config*
`MiniHipatterns.config`
Module config
Default values:
>lua
MiniHipatterns.config = {
-- Table with highlighters (see |MiniHipatterns.config| for more details).
-- Nothing is defined by default. Add manually for visible effect.
highlighters = {},
-- Delays (in ms) defining asynchronous highlighting process
delay = {
-- How much to wait for update after every text change
text_change = 200,
-- How much to wait for update after window scroll
scroll = 50,
},
}
<
# Options ~
## Highlighters ~
`highlighters` table defines which patterns will be highlighted by placing
|extmark| at the match start. It might or might not have explicitly named
fields, but having them is recommended and is required for proper use of
`vim.b.minihipatterns_config` as buffer-local config. By default it is
empty expecting user definition.
Each entry defines single highlighter as a table with the following fields:
- <pattern> `(string|function|table)` - Lua pattern to highlight. Can be
either string, callable returning the string, or an array of those.
If string:
- It can have submatch delimited by placing `()` on start and end, NOT
by surrounding with it. Otherwise it will result in error containing
`number expected, got string`. Example: `xx()abcd()xx` will match `abcd`
only if `xx` is placed before and after it.
If callable:
- It will be called for every enabled buffer with its identifier as input.
- It can return `nil` meaning this particular highlighter will not work
in this particular buffer.
If array:
- Each element is matched and highlighted with the same highlight group.
- <group> `(string|function)` - name of highlight group to use. Can be either
string or callable returning the string.
If callable:
- It will be called for every pattern match with the following arguments:
- `buf_id` - buffer identifier.
- `match` - string pattern match to be highlighted.
- `data` - extra table with information about the match.
It has at least these fields:
- <full_match> - string with full pattern match.
- <line> - match line number (1-indexed).
- <from_col> - match starting byte column (1-indexed).
- <to_col> - match ending byte column (1-indexed, inclusive).
- It can return `nil` meaning this particular match will not be highlighted.
- <extmark_opts> `(table|function|nil)` - optional extra options
for |nvim_buf_set_extmark()|. If callable, will be called in the same way
as callable <group> (`data` will also contain `hl_group` key with <group>
value) and should return a table with all options for extmark (including
`end_row`, `end_col`, `hl_group`, and `priority`).
Note: if <extmark_opts> is supplied, <priority> is ignored.
- <priority> `(number|nil)` - SOFT DEPRECATED in favor
of `extmark_opts = { priority = <value> }`.
Optional highlighting priority (as in |nvim_buf_set_extmark()|).
Default: 200. See also |vim.highlight.priorities|.
See "Common use cases" section for the examples.
## Delay ~
`delay` is a table defining delays in milliseconds used for asynchronous
highlighting process.
`delay.text_change` is used to delay highlighting updates by accumulating
them (in debounce fashion). Smaller values will lead to faster response but
more frequent updates. Bigger - slower response but less frequent updates.
`delay.scroll` is used to delay updating highlights in current window view
during scrolling (see |WinScrolled| event). These updates are present to
ensure up to date highlighting after scroll.
------------------------------------------------------------------------------
*MiniHipatterns.enable()*
`MiniHipatterns.enable`({buf_id}, {config})
Enable highlighting in buffer
Notes:
- With default config it will highlight nothing, as there are no default
highlighters.
- Buffer highlighting is enabled until buffer is unloaded from memory
or |MiniHipatterns.disable()| on this buffer is called.
- `:edit` disables this, as it is mostly equivalent to closing and opening
buffer. In order for highlighting to persist after `:edit`, call
|MiniHipatterns.setup()|.
Parameters ~
{buf_id} `(number|nil)` Buffer identifier in which to enable highlighting.
Default: 0 for current buffer.
{config} `(table|nil)` Optional buffer-local config. Should have the same
structure as |MiniHipatterns.config|. Values will be taken in this order:
- From this `config` argument (if supplied).
- From buffer-local config in `vim.b.minihipatterns_config` (if present).
- From global config (if |MiniHipatterns.setup()| was called).
- From default values.
------------------------------------------------------------------------------
*MiniHipatterns.disable()*
`MiniHipatterns.disable`({buf_id})
Disable highlighting in buffer
Note that if |MiniHipatterns.setup()| was called, the effect is present
until the next auto-enabling event. To permanently disable highlighting in
buffer, set `vim.b.minihipatterns_disable` to `true`
Parameters ~
{buf_id} `(number|nil)` Buffer identifier in which to enable highlighting.
Default: 0 for current buffer.
------------------------------------------------------------------------------
*MiniHipatterns.toggle()*
`MiniHipatterns.toggle`({buf_id}, {config})
Toggle highlighting in buffer
Call |MiniHipatterns.disable()| if enabled; |MiniHipatterns.enable()| otherwise.
Parameters ~
{buf_id} `(number|nil)` Buffer identifier in which to enable highlighting.
Default: 0 for current buffer.
{config} `(table|nil)` Forwarded to |MiniHipatterns.enable()|.
------------------------------------------------------------------------------
*MiniHipatterns.update()*
`MiniHipatterns.update`({buf_id}, {from_line}, {to_line})
Update highlighting in range
Works only in buffer with enabled highlighting. Effect takes immediately
without delay.
Parameters ~
{buf_id} `(number|nil)` Buffer identifier in which to enable highlighting.
Default: 0 for current buffer.
{from_line} `(number|nil)` Start line from which to update (1-indexed).
{to_line} `(number|nil)` End line from which to update (1-indexed, inclusive).
------------------------------------------------------------------------------
*MiniHipatterns.get_enabled_buffers()*
`MiniHipatterns.get_enabled_buffers`()
Get an array of enabled buffers
Return ~
`(table)` Array of buffer identifiers with enabled highlighting.
------------------------------------------------------------------------------
*MiniHipatterns.get_matches()*
`MiniHipatterns.get_matches`({buf_id}, {highlighters})
Get buffer matches
Parameters ~
{buf_id} `(number|nil)` Buffer identifier for which to return matches.
Default: `nil` for current buffer.
{highlighters} `(table|nil)` Array of highlighter identifiers (as in
`highlighters` field of |MiniHipatterns.config|) for which to return matches.
Default: all available highlighters (ordered by string representation).
Return ~
`(table)` Array of buffer matches which are tables with following fields:
- <bufnr> `(number)` - buffer identifier of a match.
- <highlighter> `(any)` - highlighter identifier which produced the match.
- <lnum> `(number)` - line number of the match start (starts with 1).
- <col> `(number)` - column number of the match start (starts with 1).
- <end_lnum> `(number|nil)` - line number of the match end (starts with 1).
- <end_col> `(number|nil)` - column number next to the match end
(implements end-exclusive region; starts with 1).
- <hl_group> `(string|nil)` - name of match's highlight group.
Matches are ordered first by supplied `highlighters`, then by line and
column of match start.
------------------------------------------------------------------------------
*MiniHipatterns.gen_highlighter*
`MiniHipatterns.gen_highlighter`
Generate builtin highlighters
This is a table with function elements. Call to actually get highlighter.
------------------------------------------------------------------------------
*MiniHipatterns.gen_highlighter.hex_color()*
`MiniHipatterns.gen_highlighter.hex_color`({opts})
Highlight hex color string
This will match color hex string in format `#rrggbb` and highlight it
according to `opts.style` displaying matched color.
Highlight group is computed using |MiniHipatterns.compute_hex_color_group()|,
so all its usage notes apply here.
Parameters ~
{opts} `(table|nil)` Options. Possible fields:
- <style> `(string)` - one of:
- `'full'` - highlight background of whole hex string with it. Default.
- `'#'` - highlight background of only `#`.
- `'line'` - highlight underline with that color.
- `'inline'` - highlight text of <inline_text>.
Note: requires Neovim>=0.10.
- <priority> `(number)` - priority of highlighting. Default: 200.
- <filter> `(function)` - callable object used to filter buffers in which
highlighting will take place. It should take buffer identifier as input
and return `false` or `nil` to not highlight inside this buffer.
- <inline_text> `(string)` - string to be placed and highlighted with color
to the right of match in case <style> is "inline". Default: "█".
Return ~
`(table)` Highlighter table ready to be used as part of `config.highlighters`.
Both `pattern` and `group` are callable.
Usage ~
>lua
local hipatterns = require('mini.hipatterns')
hipatterns.setup({
highlighters = {
hex_color = hipatterns.gen_highlighter.hex_color(),
}
})
<
------------------------------------------------------------------------------
*MiniHipatterns.compute_hex_color_group()*
`MiniHipatterns.compute_hex_color_group`({hex_color}, {style})
Compute and create group to highlight hex color string
Notes:
- This works properly only with enabled |termguicolors|.
- To increase performance, it caches highlight groups per `hex_color` and
`style` combination. Needs a call to |MiniHipatterns.setup()| to have
these groups be persistent across color scheme changes.
Parameters ~
{hex_color} `(string)` Hex color string in format `#rrggbb`.
{style|nil} `(string)` One of:
- `'bg'` - highlight background with `hex_color` and foreground with black or
white (whichever is more visible). Default.
- `'fg'` - highlight foreground with `hex_color`.
- `'line'` - highlight underline with `hex_color`.
Return ~
`(string)` Name of created highlight group appropriate to show `hex_color`.
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -0,0 +1,370 @@
*mini.hues* Generate configurable color scheme
*MiniHues*
MIT License Copyright (c) 2023 Evgeni Chasnovski
==============================================================================
Features:
- Required to set two base colors: background and foreground.
Their shades and other non-base colors are computed to be as much
perceptually different as reasonably possible.
See |MiniHues.config| for setup inspiration.
- Configurable:
- Number of hues used for non-base colors (from 0 to 8).
- Saturation level ('low', 'medium', 'high').
- Accent color used for some selected UI elements.
- Plugin integration (can be selectively enabled for faster startup).
- Random generator for base colors. See |MiniHues.gen_random_base_colors()|.
Powers |randomhue| color scheme.
- Lua function to compute palette used in color scheme.
See |MiniHues.make_palette()|.
Supported highlight groups:
- All built-in UI and syntax groups.
- Built-in Neovim LSP and diagnostic.
- Tree-sitter (|treesitter-highlight-groups|).
- LSP semantic tokens (|lsp-semantic-highlight|).
- Plugins (either with explicit definition or by verification that default
highlighting works appropriately):
- 'echasnovski/mini.nvim'
- 'akinsho/bufferline.nvim'
- 'anuvyklack/hydra.nvim'
- 'DanilaMihailov/beacon.nvim'
- 'folke/lazy.nvim'
- 'folke/noice.nvim'
- 'folke/todo-comments.nvim'
- 'folke/trouble.nvim'
- 'folke/which-key.nvim'
- 'ggandor/leap.nvim'
- 'glepnir/dashboard-nvim'
- 'glepnir/lspsaga.nvim'
- 'HiPhish/rainbow-delimiters.nvim'
- 'hrsh7th/nvim-cmp'
- 'justinmk/vim-sneak'
- 'kevinhwang91/nvim-ufo'
- 'lewis6991/gitsigns.nvim'
- 'lukas-reineke/indent-blankline.nvim'
- 'neoclide/coc.nvim'
- 'NeogitOrg/neogit'
- 'nvim-lualine/lualine.nvim'
- 'nvim-neo-tree/neo-tree.nvim'
- 'nvim-telescope/telescope.nvim'
- 'nvim-tree/nvim-tree.lua'
- 'phaazon/hop.nvim'
- 'rcarriga/nvim-dap-ui'
- 'rcarriga/nvim-notify'
- 'rlane/pounce.nvim'
- 'romgrk/barbar.nvim'
- 'stevearc/aerial.nvim'
- 'williamboman/mason.nvim'
# Setup ~
This module needs a setup with `require('mini.hues').setup({})` and
**mandatory `background` and `foreground` fields** (add more fields to fit
your taste). It will create global Lua table `MiniHues` which you can use
for scripting or manually (with `:lua MiniHues.*`).
See |MiniHues.config| for `config` structure and default values.
This module doesn't have runtime options, so using `vim.b.minihues_config`
will have no effect here.
Example:
>
require('mini.hues').setup({
background = '#11262d',
foreground = '#c0c8cc',
plugins = {
default = false,
['echasnovski/mini.nvim'] = true,
},
})
<
# Notes ~
- Using `setup()` doesn't actually create a |colorscheme|. It basically
creates a coordinated set of |highlight|s. To create your own scheme:
- Put "myscheme.lua" file (name after your chosen theme name) inside
any "colors" directory reachable from 'runtimepath' ("colors" inside
your Neovim config directory is usually enough).
- Inside "myscheme.lua" call `require('mini.hues').setup()` with your
palette and only after that set |g:colors_name| to "myscheme".
- This module doesn't define |cterm-colors| for implementation simplicity.
Use |mini.colors| module, |MiniColors-colorscheme:add_cterm_attributes()|
in particular.
------------------------------------------------------------------------------
*randomhue*
Random hue color scheme ~
This module comes with a pre-built color scheme but with a twist: every
`:colorscheme randomhue` call will result in a different (randomly yet
carefully selected) colors.
It is essentially a combination of calls to |MiniHues.setup()| and
|MiniHues.gen_random_base_colors()| with a slight adjustments for
'background' value.
Activate it as regular |colorscheme|. Get currently active config with
`:lua print(vim.inspect(MiniHues.config))`.
------------------------------------------------------------------------------
*MiniHues.setup()*
`MiniHues.setup`({config})
Module setup
Main side effect is to create palette and apply it. Essentially, a combination
of |MiniHues.make_palette()| and |MiniHues.apply_palette()|.
Usage ~
>lua
require('mini.hues').setup({
-- Use config table as you like
-- Needs both `background` and `foreground` fields present
background = '#11262d',
foreground = '#c0c8cc',
})
<
------------------------------------------------------------------------------
*MiniHues.config*
`MiniHues.config`
Module config
See |MiniHues.make_palette()| for more information about how certain
settings affect output color scheme.
Default values:
>lua
MiniHues.config = {
-- **Required** base colors as '#rrggbb' hex strings
background = nil,
foreground = nil,
-- Number of hues used for non-base colors
n_hues = 8,
-- Saturation level. One of 'low', 'medium', 'high'.
saturation = 'medium',
-- Accent color. One of: 'bg', 'fg', 'red', 'orange', 'yellow', 'green',
-- 'cyan', 'azure', 'blue', 'purple'
accent = 'bg',
-- Plugin integrations. Use `default = false` to disable all integrations.
-- Also can be set per plugin (see |MiniHues.config|).
plugins = { default = true },
}
<
# Options ~
## Plugin integrations ~
`config.plugins` defines for which supported plugins highlight groups will
be created. Limiting number of integrations slightly decreases startup time.
It is a table with boolean (`true`/`false`) values which are applied as follows:
- If plugin name (as listed in |mini.hues|) has entry, it is used.
- Otherwise `config.plugins.default` is used.
Example which will load only "mini.nvim" integration:
>
require('mini.hues').setup({
background = '#11262d',
foreground = '#c0c8cc',
plugins = {
default = false,
['echasnovski/mini.nvim'] = true,
},
})
# Examples ~
Here are some possible setup configurations (copy first line and then use
only one `setup` call): >
local setup = require('mini.hues').setup
-- Choose background and foreground
setup({ background = '#2f1c22', foreground = '#cdc4c6' }) -- red
setup({ background = '#2f1e16', foreground = '#cdc5c1' }) -- orange
setup({ background = '#282211', foreground = '#c9c6c0' }) -- yellow
setup({ background = '#1c2617', foreground = '#c4c8c2' }) -- green
setup({ background = '#112723', foreground = '#c0c9c7' }) -- cyan
setup({ background = '#11262d', foreground = '#c0c8cc' }) -- azure
setup({ background = '#1d2231', foreground = '#c4c6cd' }) -- blue
setup({ background = '#281e2c', foreground = '#c9c5cb' }) -- purple
-- Choose number of accent colors
setup({ background = '#11262d', foreground = '#c0c8cc', n_hues = 6 })
setup({ background = '#11262d', foreground = '#c0c8cc', n_hues = 4 })
setup({ background = '#11262d', foreground = '#c0c8cc', n_hues = 2 })
setup({ background = '#11262d', foreground = '#c0c8cc', n_hues = 0 })
-- Choose saturation of colored text
setup({ background = '#11262d', foreground = '#c0c8cc', saturation = 'low' })
setup({ background = '#11262d', foreground = '#c0c8cc', saturation = 'medium' })
setup({ background = '#11262d', foreground = '#c0c8cc', saturation = 'high' })
-- Choose accent color
setup({ background = '#11262d', foreground = '#c0c8cc', accent = 'bg' })
setup({ background = '#11262d', foreground = '#c0c8cc', accent = 'red' })
setup({ background = '#11262d', foreground = '#c0c8cc', accent = 'yellow' })
setup({ background = '#11262d', foreground = '#c0c8cc', accent = 'cyan' })
setup({ background = '#11262d', foreground = '#c0c8cc', accent = 'blue' })
------------------------------------------------------------------------------
*MiniHues.make_palette()*
`MiniHues.make_palette`({config})
Make palette
General idea of palette generation is that it is mostly based on color channel
information extracted from base colors (background and foreground).
All operations are done inside `Oklch` color space, meaning that each color
is defined by three numbers:
- Lightness (`l`) - number between 0 (black) and 100 (white) describing how
light is a color.
- Chroma (`c`) - positive number describing how colorful is a color (bigger
values - more colorful; 0 is gray).
- Hue (`h`) - periodic number in [0, 360) describing a value of "true color"
on color circle/wheel.
For more details about `Oklch` see |MiniColors-color-spaces| or
https://bottosson.github.io/posts/oklab/.
Algorithm overview ~
- Extract lightness, chroma, and hue of base colors.
- Generate reference lightness values:
- Background edge: 0 or 100, whichever is closest to background lightness.
- Foreground edge: 0 or 100, different from background edge.
- Middle: arithmetic mean of background and foreground lightness values.
- Compute background and foreground tints and shades by changing lightness
of background color: two colors closer to background lightness edge and
two closer to middle.
- Pick chroma value for non-base colors based on `config.saturation`.
- Generate hues for non-base colors:
- Fit an equidistant circular grid with `config.n_hues` points to be as
far from both background and foreground hues. This will ensure that
non-base colors are as different as possible from base ones (for
better visual perception).
Example: for background hue 0, foreground hue 180, and `config.n_hues` 2
the output grid will be `{ 90, 270 }`.
- For each hue of reference color (which itself is an equidistant grid
of 8 hues) compute the closest value from the grid. This allows
operating in same terms (like "red", "green") despite maybe actually
having less different hues.
- Compute for each hue two variants of non-base colors: with background and
foreground lightness values.
- Compute two variants of accent color (with background and foreground
lightness) based on `config.accent`.
Notes:
- Some output colors can have not exact values of generated Oklch channels.
This is due to actually computed colors being impossible to represent via
'#rrggbb' hex string. In this case a process called gamut clipping is done
to reduce lightness and chroma in optimal way while maintaining same hue.
For more information see |MiniColors-gamut-clip|.
- Not all colors are actually used in highlight groups and are present for the
sake of completeness.
Parameters ~
{config} `(table)` Configuration for palette. Same structure as |MiniHues.config|.
Needs to have <background> and <foreground> fields.
Return ~
`(table)` Palette with the following fields:
- <bg> and <fg> with supplied `background` and `foreground` colors.
- Fields like <bg_*> and <fg_*> are essentially <bg> and <fg> but with
different lightness values: `_edge`/`_edge2` - closer to edge lightness,
`_mid`/`_mid2` - closer to middle lightness.
- Fields for non-base colors (<red>, <orange>, <yellow>, <green>, <cyan>,
<azure>, <blue>, <purple>) have the same lightness as foreground.
- Fields for non-base colors with <_bg> suffix have the same lightness as
background.
- <accent> and <accent_bg> represent accent colors with foreground and
background lightness values.
------------------------------------------------------------------------------
*MiniHues.apply_palette()*
`MiniHues.apply_palette`({palette}, {plugins})
Apply palette
Create color scheme highlight groups and terminal colors based on supplied
palette. This is useful if you want to tweak palette colors.
For regular usage prefer |MiniHues.setup()|.
Parameters ~
{palette} `(table)` Table with structure as |MiniHues.make_palette()| output.
{plugins} `(table|nil)` Table with boolean values indicating whether to create
highlight groups for specific plugins. See |MiniHues.config| for more details.
Default: the value from |MiniHues.config|.
Usage ~
>lua
local palette = require('mini.hues').make_palette({
background = '#11262d',
foreground = '#c0c8cc',
})
palette.cyan = '#76e0a6'
palette.cyan_bg = '#004629'
require('mini.hues').apply_palette(palette)
<
------------------------------------------------------------------------------
*MiniHues.gen_random_base_colors()*
`MiniHues.gen_random_base_colors`({opts})
Generate random base colors
Compute background and foreground colors based on randomly generated hue
and heuristically picked lightness-chroma values.
You can recreate a similar functionality but tweaked to your taste
using |mini.colors|: >
local convert = require('mini.colors').convert
local hue = math.random(0, 359)
return {
background = convert({ l = 15, c = 3, h = hue }, 'hex'),
foreground = convert({ l = 80, c = 1, h = hue }, 'hex'),
}
Notes:
- Respects 'background' (uses different lightness and chroma values for
"dark" and "light" backgrounds).
- When used during startup, might require usage of `math.randomseed()` for
proper random generation. For example: >
local hues = require('mini.hues')
math.randomseed(vim.loop.hrtime())
hues.setup(hues.gen_random_base_colors())
Parameters ~
{opts} `(table|nil)` Options. Possible values:
- <gen_hue> `(function)` - callable which will return single number for
output hue. Can be used to limit which hues will be generated.
Default: random integer between 0 and 359.
Return ~
`(table)` Table with <background> and <foreground> fields containing
color hex strings.
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -0,0 +1,506 @@
*mini.icons* Icon provider
*MiniIcons*
MIT License Copyright (c) 2024 Evgeni Chasnovski
==============================================================================
Features:
- Provide icons with their highlighting via a single |MiniIcons.get()| for
various categories: filetype, file/directory path, extension, operating
system, LSP kind values. Icons and category defaults can be overridden.
- Configurable styles: "glyph" (icon glyphs) or "ascii" (non-glyph fallback).
- Fixed set of highlight groups (linked to built-in groups by default) for
better blend with color scheme.
- Caching for maximum performance.
- Integration with |vim.filetype.add()| and |vim.filetype.match()|.
- Mocking methods of 'nvim-tree/nvim-web-devicons' for better integrations
with plugins outside 'mini.nvim'. See |MiniIcons.mock_nvim_web_devicons()|.
- Tweaking built-in maps for "LSP kind" to include icons. In particular, this
makes |mini.completion| use icons in LSP step. See |MiniIcons.tweak_lsp_kind()|.
Notes:
- It is not a goal to become a collection of icons for as much use cases as
possible. There are specific criteria for icon data to be included as
built-in in each category (see |MiniIcons.get()|).
The main supported category is "filetype".
Recommendations for plugin authors using 'mini.icons' as a dependency:
- Check if `_G.MiniIcons` table is present (which means that user explicitly
enabled 'mini.icons') and provide icons only if it is.
- Use |MiniIcons.get()| function to get icon string and more data about it.
- For file icons prefer using full path instead of relative or only basename.
It makes a difference if path matches pattern that uses parent directories.
The |MiniIcons.config| has an example of that.
# Dependencies ~
Suggested dependencies:
- Terminal emulator that supports showing special utf8 glyphs, possibly with
"overflow" view (displaying is done not in one but two visual cells).
Most modern feature-rich terminal emulators support this out of the box:
WezTerm, Kitty, Alacritty, iTerm2, Ghostty.
Not having "overflow" feature only results into smaller icons.
Not having support for special utf8 glyphs will result into seemingly
random symbols (or question mark squares) instead of icon glyphs.
- Font that supports Nerd Fonts (https://www.nerdfonts.com) icons from
version 3.0.0+ (in particular `nf-md-*` class).
This should be configured on terminal emulator level either by using font
patched with Nerd Fonts icons or using `NerdFontsSymbolsOnly` font as
a fallback for glyphs that are not supported in main font.
If using terminal emulator and/or font with icon support is impossible, use
`config.style = 'ascii'`. It will use a (less visually appealing) set of
non-glyph icons.
# Setup ~
This module needs a setup with `require('mini.icons').setup({})` (replace `{}`
with your `config` table). It will create global Lua table `MiniIcons` which you
can use for scripting or manually (with `:lua MiniIcons.*`).
See |MiniIcons.config| for `config` structure and default values.
# Comparisons ~
- 'nvim-tree/nvim-web-devicons' (for users):
- Sets individual colors to each icon with separate specific highlight
groups, while this modules uses fixed set of highlight groups.
This makes it easier to customize in bulk and actually blend with any
color scheme.
- This module prefers richer set of `nf-md-*` (from "Material design" set)
Nerd Fonts icons while 'nvim-web-devicons' mostly prefers `nf-dev-*`
(from "devicons" set).
- Supported categories are slightly different (with much overlap).
- Both support customization of any icon. Only this module supports
customization of default ones per supported category.
- Using this module can occasionally result in small delays when used
synchronously for many times to get icons for not typical files (like
in |mini.files|). This is due to using |vim.filetype.match()| fallback and
is present only during first call, as value is cached for later uses.
- This module supports different icon styles (like "ascii" for when using
glyphs is not possible), while 'nvim-web-devicons' does not.
- This module provides |MiniIcons.mock_nvim_web_devicons()| function which
when called imitates installed 'nvim-web-devicons' plugin to support
other plugins which do not provide 'mini.icons' yet.
- 'nvim-tree/nvim-web-devicons' (for plugin developers):
- Both have main "get icon" type of function:
- Both return tuple of icon and highlight group strings.
- This module always returns icon data possibly falling back to
user's configured default, while 'nvim-web-devicons' is able to
return `nil`. This module's approach is more aligned with the most
common use case of always showing an icon instead or near some data.
There is a third returned value indicating if output is a result of
a fallback (see |MiniIcons.get()|).
- This module uses |vim.filetype.match()| as a fallback for "file"
and "extension" categories, while 'nvim-web-devicons' completely
relies on the manually maintained tables of supported filenames
and extensions.
Using fallback results in a wider support and deeper integration
with Neovim's filetype detection at the cost of occasional slower
first call. The difference is reduced as much as is reasonable by
preferring faster file extension resolution over filetype matching.
- This module caches all its return values resulting in really fast
next same argument calls, while 'nvim-web-devicons' doesn't do that.
- This module works with full file/directory paths as input.
- Different sets of supported categories (see |MiniIcons.config|):
- Both support "file", "extension", "filetype", "operating system".
Albeit in different volumes: 'nvim-web-devicons' covers more
cases for "operating system", while this module has better eventual
coverage for other cases.
- This module supports "directory" and "lsp" categories.
- 'nvim-web-devicons' covers "desktop environment" and "window
management" categories. This modules does not include them due to
relatively low demand.
- 'onsails/lspkind.nvim':
- Provides icons only for `CompletionItemKind`, while this module also has
icons for `SymbolKind` and other non-LSP categories.
- Provides dedicated formatting function for 'hrsh7th/nvim-cmp' while this
module intentionally does not (adding icons should be straightforward
to manually implement while anything else is out of scope).
# Highlight groups ~
Only the following set of highlight groups is used as icon highlight.
It is recommended that they all only define colored foreground:
* `MiniIconsAzure` - azure.
* `MiniIconsBlue` - blue.
* `MiniIconsCyan` - cyan.
* `MiniIconsGreen` - green.
* `MiniIconsGrey` - grey.
* `MiniIconsOrange` - orange.
* `MiniIconsPurple` - purple.
* `MiniIconsRed` - red.
* `MiniIconsYellow` - yellow.
To change any highlight group, modify it directly with |:highlight|.
------------------------------------------------------------------------------
*MiniIcons.setup()*
`MiniIcons.setup`({config})
Module setup
Parameters ~
{config} `(table|nil)` Module config table. See |MiniIcons.config|.
Usage ~
>lua
require('mini.icons').setup() -- use default config
-- OR
require('mini.icons').setup({}) -- replace {} with your config table
<
------------------------------------------------------------------------------
*MiniIcons.config*
`MiniIcons.config`
Module config
Default values:
>lua
MiniIcons.config = {
-- Icon style: 'glyph' or 'ascii'
style = 'glyph',
-- Customize per category. See `:h MiniIcons.config` for details.
default = {},
directory = {},
extension = {},
file = {},
filetype = {},
lsp = {},
os = {},
-- Control which extensions will be considered during "file" resolution
use_file_extension = function(ext, file) return true end,
}
<
# Style ~
`config.style` is a string defining which icon style to use. It can be:
- `'glyph'` (default) - use glyph icons (like 󰈔 and 󰉋 ).
- `'ascii'` - use fallback ASCII-compatible icons. Those are computed as
an upper first character of the icon's resolved name inside its category.
Examples: >lua
MiniIcons.get('file', 'Makefile') -- Has `'M'` as icon
MiniIcons.get('extension', 'lua') -- Has `'L'` as icon
MiniIcons.get('file', 'file.lua') -- Has `'L'` as icon; it is resolved to
-- come from 'lua' 'extension' category
MiniIcons.get('file', 'myfile') -- Has `'F'` as icon; it is resolved to
-- come from 'file' 'default' category
<
# Customization per category ~
The following entries can be used to customize icons for supported categories:
- `config.default`
- `config.directory`
- `config.extension`
- `config.file`
- `config.filetype`
- `config.lsp`
- `config.os`
Customization should be done by supplying a table with <glyph> (icon glyph)
and/or <hl> (name of highlight group) string fields as a value for an icon
name entry. Example: >lua
require('mini.icons').setup({
default = {
-- Override default glyph for "file" category (reuse highlight group)
file = { glyph = '󰈤' },
},
extension = {
-- Override highlight group (not necessary from 'mini.icons')
lua = { hl = 'Special' },
-- Add icons for custom extension. This will also be used in
-- 'file' category for input like 'file.my.ext'.
['my.ext'] = { glyph = '󰻲', hl = 'MiniIconsRed' },
},
})
<
Notes:
- These customizations only take effect inside |MiniIcons.setup()| call.
Changing interactively via `:lua MiniIcons.config.xxx = { ... }` does not work
for performance reasons.
- Use lower case names for categories which are matched ignoring case.
See |MiniIcons.get()| for more details.
# Using extension during file resolution ~
`config.use_file_extension` is a function which can be used to control which
extensions will be considered as a source of icon data during "file" category
resolution (see |MiniIcons.get()| for more details).
Default: function which always returns `true` (i.e. consider all extensions).
Will be called once for the biggest suffix after dot found in the file name.
The arguments will be `ext` (found extension; lowercase) and `file` (input for
which icon is computed; as is). Should explicitly return `true` if `ext` is to
be considered (i.e. call `MiniIcons.get('extension', ext)` and use its
output if it is not default). Otherwise extension won't be even considered.
The primary use case for this setting is to ensure that some extensions are
ignored in order for resolution to reach |vim.filetype.match()| stage. This
is needed if there is a set up filetype detection for files with recognizable
extension and conflicting icons (which you want to use). Note: if problematic
filetype detection involves only known in advance file names, prefer using
`config.file` customization.
Example: >lua
-- Built-in filetype detection recognizes files like "queries/.*%.scm"
-- as "query" filetype. However, without special setup, 'mini.icons' will
-- use "scm" extension to resolve as Scheme file. Here is a setup to ignore
-- "scm" extension and completely rely on `vim.filetype.match()` fallback.
require('mini.icons').setup({
-- Check last letters explicitly to account for dots in file name
use_file_extension = function(ext) return ext:sub(-3) ~= 'scm' end
})
-- Another common choices for extensions to ignore: "yml", "json", "txt".
<
------------------------------------------------------------------------------
*MiniIcons.get()*
`MiniIcons.get`({category}, {name})
Get icon data
Usage example: >lua
-- Results into `icon='󰢱'`, `hl='MiniIconsAzure'`, `is_default=false`
local icon, hl, is_default = MiniIcons.get('file', 'file.lua')
<
Notes:
- Always returns some data, even if icon name is not explicitly supported
within target category. Category "default" is used as a fallback. Use third
output value to check if this particular case is a result of a fallback.
- Glyphs are explicitly preferred (when reasonable) from a richer set of
`nf-md-*` class ("Material design" set) of Nerd Fonts icons.
- Output is cached after the first call to increase performance of next calls
with same arguments. To reset cache, call |MiniIcons.setup()|.
- To increase first call performance for "extension" and "file" categories,
add frequently used values in |MiniIcons.config|. They will be preferred
over executing |vim.filetype.match()|.
- Matching icon name for "file" and "directory" categories is done exactly
and respecting case. Others are done ignoring case.
Parameters ~
{category} `(string)` Category name. Supported categories:
- `'default'` - icon data used as fallback for any category.
Icon names:
- <Input>: any supported category name.
- <Built-in>: only supported category names.
Examples: >lua
MiniIcons.get('default', 'file')
<
- `'directory'` - icon data for directory path.
Icon names:
- <Input>: any string, but only basename is used. Works with not present
paths (no check is done).
- <Built-in>: popular directory names not tied to language/software
(with few notable exceptions like Neovim, Git, etc.).
Examples: >lua
-- All of these will result in the same output
MiniIcons.get('directory', '.config')
MiniIcons.get('directory', '~/.config')
MiniIcons.get('directory', '/home/user/.config')
-- Results in different output
MiniIcons.get('directory', '.Config')
<
- `'extension'` - icon data for extension.
Icon names:
- <Input>: any string (without extra dot prefix).
- <Built-in>: popular extensions without associated filetype plus a set
for which filetype detection gives not good enough result.
Icon data is attempted to be resolved in the following order:
- List of user configured and built-in extensions (for better results).
Run `:=MiniIcons.list('extension')` to see them.
Used also if present as suffix after the dot (widest one preferred).
- Filetype as a result of |vim.filetype.match()| with placeholder
file name. Uses icon data from "filetype" category.
Examples: >lua
-- All of these will result in the same output
MiniIcons.get('extension', 'lua')
MiniIcons.get('extension', 'LUA')
MiniIcons.get('extension', 'my.lua')
<
- `'file'` - icon data for file path.
Icon names:
- <Input>: any string. Works with not present paths (no check is done).
- <Built-in>: popular file names not tied to language/software
(with few notable exceptions like Neovim, Git, etc.) plus a set which
has recognizable extension but has special detectable filetype.
Icon data is attempted to be resolved in the following order:
- List of user configured and built-in file names (matched to basename
of the input exactly). Run `:=MiniIcons.list('flle')` to see them.
- Basename extension:
- Matched directly as `get('extension', ext)`, where `ext` is the
widest suffix after the dot.
- Considered only if `config.use_file_extension` returned `true`.
- Only recognizable extensions (i.e. not default fallback) are used.
- Filetype as a result of |vim.filetype.match()| with full input (not
basename) as `filename`. Uses icon data from "filetype" category.
Examples: >lua
-- All of these will result in the same output
MiniIcons.get('file', 'init.lua')
MiniIcons.get('file', '~/.config/nvim/init.lua')
MiniIcons.get('file', '/home/user/.config/nvim/init.lua')
-- Results in different output
MiniIcons.get('file', 'Init.lua')
MiniIcons.get('file', 'init.LUA')
-- Respects full path input in `vim.filetype.match()`
MiniIcons.get('file', '.git/info/attributes')
<
- `'filetype'` - icon data for 'filetype' values.
Icon names:
- <Input>: any string.
- <Built-in>: any filetype that is reasonably used in Neovim ecosystem.
This category is intended as a widest net for supporting use cases.
Users are encouraged to have a specific filetype detection set up.
Examples: >lua
MiniIcons.get('filetype', 'lua')
MiniIcons.get('filetype', 'help')
MiniIcons.get('filetype', 'minifiles')
<
- `'lsp'` - icon data for various "LSP kind" values.
Icon names:
- <Input>: any string.
- <Built-in>: only namesspace entries from LSP specification that are
can be displayed to user. Like `CompletionItemKind`, `SymbolKind`, etc.
Examples: >lua
MiniIcons.get('lsp', 'array')
MiniIcons.get('lsp', 'keyword')
<
- `'os'` - icon data for popular operating systems.
Icon names:
- <Input>: any string.
- <Built-in>: only operating systems which have `nf-md-*` class icon.
Examples: >lua
MiniIcons.get('os', 'linux')
MiniIcons.get('os', 'arch')
MiniIcons.get('os', 'macos')
<
{name} `(string)` Icon name within category. Use |MiniIcons.list()| to get icon
names which are explicitly supported for specific category.
Return ~
`(...)` Tuple of icon string, highlight group name it is suggested to be
highlighted with, and boolean indicating whether this icon was returned
as a result of fallback to default. Example: >lua
-- Results into `icon='󰢱'`, `hl='MiniIconsAzure'`, `is_default=false`
local icon, hl, is_default = MiniIcons.get('file', 'file.lua')
-- Results into `icon='󰈔'`, `hl='MiniIconsGrey'`, `is_default=true`
local icon, hl, is_default = MiniIcons.get('file', 'not-supported')
<
------------------------------------------------------------------------------
*MiniIcons.list()*
`MiniIcons.list`({category})
List explicitly supported icon names
Parameters ~
{category} `(string)` Category name supported by |MiniIcons.get()|.
Return ~
`(table)` Array of icon names which are explicitly supported for category.
Note, that `'file'` and `'extension'` categories support much more icon names
via their fallback to using |vim.filetype.match()| with `'filetype'` category.
------------------------------------------------------------------------------
*MiniIcons.mock_nvim_web_devicons()*
`MiniIcons.mock_nvim_web_devicons`()
Mock 'nvim-web-devicons' module
Call this function to mock exported functions of 'nvim-tree/nvim-web-devicons'
plugin. It will mock all its functions which return icon data by
using |MiniIcons.get()| equivalent.
This function is useful if any plugins relevant to you depend solely on
'nvim-web-devicons' and have not yet added an integration with 'mini.icons'.
Full example of usage: >lua
require('mini.icons').setup()
MiniIcons.mock_nvim_web_devicons()
<
Works without installed 'nvim-web-devicons' and even with it installed (needs
to be called after 'nvim-web-devicons' is set up).
------------------------------------------------------------------------------
*MiniIcons.tweak_lsp_kind()*
`MiniIcons.tweak_lsp_kind`({mode})
Tweak built-in LSP kind names
Update in place appropriate maps in |vim.lsp.protocol| (`CompletionItemKind`
and `SymbolKind`) by using icon strings from "lsp" category. Only "numeric
id to kind name" part is updated (to preserve data from original map).
Updating is done in one of these modes:
- Append: add icon after text.
- Prepend: add icon before text (default).
- Replace: use icon instead of text.
Notes:
- Makes |mini.completion| show icons, as it uses built-in protocol map.
- Results in loading whole `vim.lsp` module, so might add significant amount
of time on startup. Call it lazily. For example, with |MiniDeps.later()|: >
require('mini.icons').setup()
MiniDeps.later(MiniIcons.tweak_lsp_kind)
<
Parameters ~
{mode} `(string|nil)` One of "prepend" (default), "append", "replace".
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -0,0 +1,469 @@
*mini.indentscope* Visualize and work with indent scope
*MiniIndentscope*
MIT License Copyright (c) 2022 Evgeni Chasnovski
==============================================================================
Indent scope (or just "scope") is a maximum set of consecutive lines which
contains certain reference line (cursor line by default) and every member
has indent not less than certain reference indent ("indent at cursor" by
default: minimum between cursor column and indent of cursor line).
Features:
- Visualize scope with animated vertical line. It is very fast and done
automatically in a non-blocking way (other operations can be performed,
like moving cursor). You can customize debounce delay and animation rule.
- Customization of scope computation options can be done on global level
(in |MiniIndentscope.config|), for a certain buffer (using
`vim.b.miniindentscope_config` buffer variable), or within a call (using
`opts` variable in |MiniIndentscope.get_scope|).
- Customizable notion of a border: which adjacent lines with strictly lower
indent are recognized as such. This is useful for a certain filetypes
(for example, Python or plain text).
- Customizable way of line to be considered "border first". This is useful
if you want to place cursor on function header and get scope of its body.
- There are textobjects and motions to operate on scope. Support |count|
and dot-repeat (in operator pending mode).
# Setup ~
This module needs a setup with `require('mini.indentscope').setup({})`
(replace `{}` with your `config` table). It will create global Lua table
`MiniIndentscope` which you can use for scripting or manually (with `:lua
MiniIndentscope.*`).
See |MiniIndentscope.config| for available config settings.
You can override runtime config settings locally to buffer inside
`vim.b.miniindentscope_config` which should have same structure as
`MiniIndentscope.config`. See |mini.nvim-buffer-local-config| for more
details.
# Comparisons ~
- 'lukas-reineke/indent-blankline.nvim':
- Its main functionality is about showing static guides of indent levels.
- Implementation of 'mini.indentscope' is similar to
'indent-blankline.nvim' (using |extmarks| on first column to be shown
even on blank lines). They can be used simultaneously, but it will
lead to one of the visualizations being on top (hiding) of another.
# Highlight groups ~
* `MiniIndentscopeSymbol` - symbol showing on every line of scope if its
indent is multiple of 'shiftwidth'.
* `MiniIndentscopeSymbolOff` - symbol showing on every line of scope if its
indent is not multiple of 'shiftwidth'.
Default: links to `MiniIndentscopeSymbol`.
To change any highlight group, modify it directly with |:highlight|.
# Disabling ~
To disable autodrawing, set `vim.g.miniindentscope_disable` (globally) or
`vim.b.miniindentscope_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.
------------------------------------------------------------------------------
*MiniIndentscope-drawing*
Drawing of scope indicator
Draw of scope indicator is done as iterative animation. It has the
following design:
- Draw indicator on origin line (where cursor is at) immediately. Indicator
is visualized as `MiniIndentscope.config.symbol` placed to the right of
scope's border indent. This creates a line from top to bottom scope edges.
- Draw upward and downward concurrently per one line. Progression by one
line in both direction is considered to be one step of animation.
- Before each step wait certain amount of time, which is decided by
"animation function". It takes next and total step numbers (both are one
or bigger) and returns number of milliseconds to wait before drawing next
step. Comparing to a more popular "easing functions" in animation (input:
duration since animation start; output: percent of animation done), it is
a discrete inverse version of its derivative. Such interface proved to be
more appropriate for kind of task at hand.
Special cases ~
- When scope to be drawn intersects (same indent, ranges overlap) currently
visible one (at process or finished drawing), drawing is done immediately
without animation. With most common example being typing new text, this
feels more natural.
- Scope for the whole buffer is not drawn as it is isually redundant.
Technically, it can be thought as drawn at column 0 (because border
indent is -1) which is not visible.
------------------------------------------------------------------------------
*MiniIndentscope.setup()*
`MiniIndentscope.setup`({config})
Module setup
Parameters ~
{config} `(table|nil)` Module config table. See |MiniIndentscope.config|.
Usage ~
>lua
require('mini.indentscope').setup() -- use default config
-- OR
require('mini.indentscope').setup({}) -- replace {} with your config table
<
------------------------------------------------------------------------------
*MiniIndentscope.config*
`MiniIndentscope.config`
Module config
Default values:
>lua
MiniIndentscope.config = {
-- Draw options
draw = {
-- Delay (in ms) between event and start of drawing scope indicator
delay = 100,
-- Animation rule for scope's first drawing. A function which, given
-- next and total step numbers, returns wait time (in ms). See
-- |MiniIndentscope.gen_animation| for builtin options. To disable
-- animation, use `require('mini.indentscope').gen_animation.none()`.
animation = --<function: implements constant 20ms between steps>,
-- Symbol priority. Increase to display on top of more symbols.
priority = 2,
},
-- Module mappings. Use `''` (empty string) to disable one.
mappings = {
-- Textobjects
object_scope = 'ii',
object_scope_with_border = 'ai',
-- Motions (jump to respective border line; if not present - body line)
goto_top = '[i',
goto_bottom = ']i',
},
-- Options which control scope computation
options = {
-- Type of scope's border: which line(s) with smaller indent to
-- categorize as border. Can be one of: 'both', 'top', 'bottom', 'none'.
border = 'both',
-- Whether to use cursor column when computing reference indent.
-- Useful to see incremental scopes with horizontal cursor movements.
indent_at_cursor = true,
-- Whether to first check input line to be a border of adjacent scope.
-- Use it if you want to place cursor on function header to get scope of
-- its body.
try_as_border = false,
},
-- Which character to use for drawing scope indicator
symbol = '╎',
}
<
# Options ~
- Options can be supplied globally (from this `config`), locally to buffer
(via `options` field of `vim.b.miniindentscope_config` buffer variable),
or locally to call (as argument to |MiniIndentscope.get_scope()|).
- Option `border` controls which line(s) with smaller indent to categorize
as border. This matters for textobjects and motions.
It also controls how empty lines are treated: they are included in scope
only if followed by a border. Another way of looking at it is that indent
of blank line is computed based on value of `border` option.
Here is an illustration of how `border` works in presence of empty lines: >
|both|bottom|top|none|
1|function foo() | 0 | 0 | 0 | 0 |
2| | 4 | 0 | 4 | 0 |
3| print('Hello world') | 4 | 4 | 4 | 4 |
4| | 4 | 4 | 2 | 2 |
5| end | 2 | 2 | 2 | 2 |
<
Numbers inside a table are indent values of a line computed with certain
value of `border`. So, for example, a scope with reference line 3 and
right-most column has body range depending on value of `border` option:
- `border` is "both": range is 2-4, border is 1 and 5 with indent 2.
- `border` is "top": range is 2-3, border is 1 with indent 0.
- `border` is "bottom": range is 3-4, border is 5 with indent 0.
- `border` is "none": range is 3-3, border is empty with indent `nil`.
- Option `indent_at_cursor` controls if cursor position should affect
computation of scope. If `true`, reference indent is a minimum of
reference line's indent and cursor column. In main example, here how
scope's body range differs depending on cursor column and `indent_at_cursor`
value (assuming cursor is on line 3 and it is whole buffer): >
Column\Option true|false
1 and 2 2-5 | 2-4
3 and more 2-4 | 2-4
<
- Option `try_as_border` controls how to act when input line can be
recognized as a border of some neighbor indent scope. In main example,
when input line is 1 and can be recognized as border for inner scope,
value `try_as_border = true` means that inner scope will be returned.
Similar, for input line 5 inner scope will be returned if it is
recognized as border.
------------------------------------------------------------------------------
*MiniIndentscope.get_scope()*
`MiniIndentscope.get_scope`({line}, {col}, {opts})
Compute indent scope
Indent scope (or just "scope") is a maximum set of consecutive lines which
contains certain reference line (cursor line by default) and every member
has indent not less than certain reference indent ("indent at column" by
default). Here "indent at column" means minimum between input column value
and indent of reference line. When using cursor column, this allows for a
useful interactive view of nested indent scopes by making horizontal
movements within line.
Options controlling actual computation is taken from these places in order:
- Argument `opts`. Use it to ensure independence from other sources.
- Buffer local variable `vim.b.miniindentscope_config` (`options` field).
Useful to define local behavior (for example, for a certain filetype).
- Global options from |MiniIndentscope.config|.
Algorithm overview ~
- Compute reference "indent at column". Reference line is an input `line`
which might be modified to one of its neighbors if `try_as_border` option
is `true`: if it can be viewed as border of some neighbor scope, it will.
- Process upwards and downwards from reference line to search for line with
indent strictly less than reference one. This is like casting rays up and
down from reference line and reference indent until meeting "a wall"
(character to the right of indent or buffer edge). Latest line before
meeting is a respective end of scope body. It always exists because
reference line is a such one.
- Based on top and bottom lines with strictly lower indent, construct
scopes's border. The way it is computed is decided based on `border`
option (see |MiniIndentscope.config| for more information).
- Compute border indent as maximum indent of border lines (or reference
indent minus one in case of no border). This is used during drawing
visual indicator.
Indent computation ~
For every line indent is intended to be computed unambiguously:
- For "normal" lines indent is an output of |indent()|.
- Indent is `-1` for imaginary lines 0 and past last line.
- For blank and empty lines indent is computed based on previous
(|prevnonblank()|) and next (|nextnonblank()|) non-blank lines. The way
it is computed is decided based on `border` in order to not include blank
lines at edge of scope's body if there is no border there. See
|MiniIndentscope.config| for a details example.
Parameters ~
{line} `(number|nil)` Input line number (starts from 1). Can be modified to a
neighbor if `try_as_border` is `true`. Default: cursor line.
{col} `(number|nil)` Column number (starts from 1). Default: if
`indent_at_cursor` option is `true` - cursor column from `curswant` of
|getcurpos()| (allows for more natural behavior on empty lines);
`math.huge` otherwise in order to not incorporate cursor in computation.
{opts} `(table|nil)` Options to override global or buffer local ones (see
|MiniIndentscope.config|).
Return ~
`(table)` Table with scope information:
- <body> - table with <top> (top line of scope, inclusive), <bottom>
(bottom line of scope, inclusive), and <indent> (minimum indent within
scope) keys. Line numbers start at 1.
- <border> - table with <top> (line of top border, might be `nil`),
<bottom> (line of bottom border, might be `nil`), and <indent> (indent
of border) keys. Line numbers start at 1.
- <buf_id> - identifier of current buffer.
- <reference> - table with <line> (reference line), <column> (reference
column), and <indent> ("indent at column") keys.
------------------------------------------------------------------------------
*MiniIndentscope.draw()*
`MiniIndentscope.draw`({scope}, {opts})
Draw scope manually
Scope is visualized as a vertical line within scope's body range at column
equal to border indent plus one (or body indent if border is absent).
Numbering starts from one.
Parameters ~
{scope} `(table|nil)` Scope. Default: output of |MiniIndentscope.get_scope|
with default arguments.
{opts} `(table|nil)` Options. Currently supported:
- <animation_fun> - animation function for drawing. See
|MiniIndentscope-drawing| and |MiniIndentscope.gen_animation|.
- <priority> - priority number for visualization. See `priority` option
for |nvim_buf_set_extmark()|.
------------------------------------------------------------------------------
*MiniIndentscope.undraw()*
`MiniIndentscope.undraw`()
Undraw currently visible scope manually
------------------------------------------------------------------------------
*MiniIndentscope.gen_animation*
`MiniIndentscope.gen_animation`
Generate builtin animation function
This is a builtin source to generate animation function for usage in
`MiniIndentscope.config.draw.animation`. Most of them are variations of
common easing functions, which provide certain type of progression for
revealing scope visual indicator.
Each field corresponds to one family of progression which can be customized
further by supplying appropriate arguments.
Examples ~
- Don't use animation: `MiniIndentscope.gen_animation.none()`
- Use quadratic "out" easing with total duration of 1000 ms: >lua
gen_animation.quadratic({ easing = 'out', duration = 1000, unit = 'total' })
<
See also ~
|MiniIndentscope-drawing| for more information about how drawing is done.
------------------------------------------------------------------------------
*MiniIndentscope.gen_animation.none()*
`MiniIndentscope.gen_animation.none`()
Generate no animation
Show indicator immediately. Same as animation function always returning 0.
------------------------------------------------------------------------------
*MiniIndentscope.gen_animation.linear()*
`MiniIndentscope.gen_animation.linear`({opts})
Generate linear progression
Parameters ~
{opts} `(table|nil)` Options that control progression. Possible keys:
- <easing> `(string)` - a subtype of progression. One of "in"
(accelerating from zero speed), "out" (decelerating to zero speed),
"in-out" (default; accelerating halfway, decelerating after).
- <duration> `(number)` - duration (in ms) of a unit. Default: 20.
- <unit> `(string)` - which unit's duration `opts.duration` controls. One
of "step" (default; ensures average duration of step to be `opts.duration`)
or "total" (ensures fixed total duration regardless of scope's range).
Return ~
`(function)` Animation function (see |MiniIndentscope-drawing|).
------------------------------------------------------------------------------
*MiniIndentscope.gen_animation.quadratic()*
`MiniIndentscope.gen_animation.quadratic`({opts})
Generate quadratic progression
Parameters ~
{opts} `(table|nil)` Options that control progression. Possible keys:
- <easing> `(string)` - a subtype of progression. One of "in"
(accelerating from zero speed), "out" (decelerating to zero speed),
"in-out" (default; accelerating halfway, decelerating after).
- <duration> `(number)` - duration (in ms) of a unit. Default: 20.
- <unit> `(string)` - which unit's duration `opts.duration` controls. One
of "step" (default; ensures average duration of step to be `opts.duration`)
or "total" (ensures fixed total duration regardless of scope's range).
Return ~
`(function)` Animation function (see |MiniIndentscope-drawing|).
------------------------------------------------------------------------------
*MiniIndentscope.gen_animation.cubic()*
`MiniIndentscope.gen_animation.cubic`({opts})
Generate cubic progression
Parameters ~
{opts} `(table|nil)` Options that control progression. Possible keys:
- <easing> `(string)` - a subtype of progression. One of "in"
(accelerating from zero speed), "out" (decelerating to zero speed),
"in-out" (default; accelerating halfway, decelerating after).
- <duration> `(number)` - duration (in ms) of a unit. Default: 20.
- <unit> `(string)` - which unit's duration `opts.duration` controls. One
of "step" (default; ensures average duration of step to be `opts.duration`)
or "total" (ensures fixed total duration regardless of scope's range).
Return ~
`(function)` Animation function (see |MiniIndentscope-drawing|).
------------------------------------------------------------------------------
*MiniIndentscope.gen_animation.quartic()*
`MiniIndentscope.gen_animation.quartic`({opts})
Generate quartic progression
Parameters ~
{opts} `(table|nil)` Options that control progression. Possible keys:
- <easing> `(string)` - a subtype of progression. One of "in"
(accelerating from zero speed), "out" (decelerating to zero speed),
"in-out" (default; accelerating halfway, decelerating after).
- <duration> `(number)` - duration (in ms) of a unit. Default: 20.
- <unit> `(string)` - which unit's duration `opts.duration` controls. One
of "step" (default; ensures average duration of step to be `opts.duration`)
or "total" (ensures fixed total duration regardless of scope's range).
Return ~
`(function)` Animation function (see |MiniIndentscope-drawing|).
------------------------------------------------------------------------------
*MiniIndentscope.gen_animation.exponential()*
`MiniIndentscope.gen_animation.exponential`({opts})
Generate exponential progression
Parameters ~
{opts} `(table|nil)` Options that control progression. Possible keys:
- <easing> `(string)` - a subtype of progression. One of "in"
(accelerating from zero speed), "out" (decelerating to zero speed),
"in-out" (default; accelerating halfway, decelerating after).
- <duration> `(number)` - duration (in ms) of a unit. Default: 20.
- <unit> `(string)` - which unit's duration `opts.duration` controls. One
of "step" (default; ensures average duration of step to be `opts.duration`)
or "total" (ensures fixed total duration regardless of scope's range).
Return ~
`(function)` Animation function (see |MiniIndentscope-drawing|).
------------------------------------------------------------------------------
*MiniIndentscope.move_cursor()*
`MiniIndentscope.move_cursor`({side}, {use_border}, {scope})
Move cursor within scope
Cursor is placed on a first non-blank character of target line.
Parameters ~
{side} `(string)` One of "top" or "bottom".
{use_border} `(boolean|nil)` Whether to move to border or within scope's body.
If particular border is absent, body is used.
{scope} `(table|nil)` Scope to use. Default: output of |MiniIndentscope.get_scope()|.
------------------------------------------------------------------------------
*MiniIndentscope.operator()*
`MiniIndentscope.operator`({side}, {add_to_jumplist})
Function for motion mappings
Move to a certain side of border. Respects |count| and dot-repeat (in
operator-pending mode). Doesn't move cursor for scope that is not shown
(drawing indent less that zero).
Parameters ~
{side} `(string)` One of "top" or "bottom".
{add_to_jumplist} `(boolean|nil)` Whether to add movement to jump list. It is
`true` only for Normal mode mappings.
------------------------------------------------------------------------------
*MiniIndentscope.textobject()*
`MiniIndentscope.textobject`({use_border})
Function for textobject mappings
Respects |count| and dot-repeat (in operator-pending mode). Doesn't work
for scope that is not shown (drawing indent less that zero).
Parameters ~
{use_border} `(boolean|nil)` Whether to include border in textobject. When
`true` and `try_as_border` option is `false`, allows "chaining" calls for
incremental selection.
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -0,0 +1,179 @@
*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.
------------------------------------------------------------------------------
*MiniJump.setup()*
`MiniJump.setup`({config})
Module setup
Parameters ~
{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.config*
`MiniJump.config`
Module config
Default values:
>lua
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,
}
<
------------------------------------------------------------------------------
*MiniJump.state*
`MiniJump.state`
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}
Fields ~
{target} `(string|nil)` The string to jump to.
{backward} `(boolean|nil)` Whether to jump backward.
{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.
{n_times} `(number|nil)` Number of times to perform consecutive jumps.
{mode} `(string)` Mode of latest jump (output of |mode()| with non-zero argument).
{jumping} `(boolean)` Whether module is currently in "jumping mode": usage of
|MiniJump.smart_jump| and all mappings won't require target.
Initial values:
>lua
MiniJump.state = {
target = nil,
backward = false,
till = false,
n_times = 1,
mode = nil,
jumping = false,
}
<
------------------------------------------------------------------------------
*MiniJump.jump()*
`MiniJump.jump`({target}, {backward}, {till}, {n_times})
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.
Parameters ~
{target} `(string|nil)` The string to jump to.
{backward} `(boolean|nil)` Whether to jump backward.
{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.
{n_times} `(number|nil)` Number of times to perform consecutive jumps.
------------------------------------------------------------------------------
*MiniJump.smart_jump()*
`MiniJump.smart_jump`({backward}, {till})
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.
Parameters ~
{backward} `(boolean|nil)` Whether to jump backward.
{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.
------------------------------------------------------------------------------
*MiniJump.stop_jumping()*
`MiniJump.stop_jumping`()
Stop jumping
Removes highlights (if any) and forces the next smart jump to prompt for
the target. Automatically called on appropriate Neovim |events|.
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -0,0 +1,496 @@
*mini.jump2d* Jump within visible lines
*MiniJump2d*
MIT License Copyright (c) 2022 Evgeni Chasnovski
==============================================================================
Jump within visible lines via iterative label filtering.
Features:
- Make jump by iterative filtering of possible, equally considered jump
spots until there is only one. Filtering is done by typing a label
character that is visualized at jump spot.
- Customizable (see |MiniJump2d.config|):
- Way of computing possible jump spots with opinionated default.
- Characters used to label jump spots during iterative filtering.
- Visual effects: how many steps ahead to show; dim lines with spots.
- Action hooks to be executed at certain events during jump.
- Allowed windows: current and/or not current.
- Allowed lines: whether to process blank or folded lines, lines
before/at/after cursor line, etc. Example: user can configure to look
for spots only inside current window at or after cursor line.
Example: user can configure to look for word starts only inside current
window at or after cursor line with 'j' and 'k' labels performing some
action after jump.
- Works in Visual and Operator-pending (with dot-repeat) modes.
- Preconfigured ways of computing jump spots (see |MiniJump2d.builtin_opts|):
- Starts of lines.
- Starts of words.
- Single character from user input.
- Variable length query from user input.
- Works with multibyte characters.
General overview of how jump is intended to be performed:
- Lock eyes on desired location ("spot") recognizable by future jump.
Should be within visible lines at place where cursor can be placed.
- Initiate jump. Either by custom keybinding or with a call to
|MiniJump2d.start()| (allows customization options). This will highlight
all possible jump spots with their labels (letters from "a" to "z" by
default). For more details, read |MiniJump2d.start()| and |MiniJump2d.config|.
- Type character that appeared over desired location. If its label was
unique, jump is performed. If it wasn't unique, possible jump spots are
filtered to those having the same label character.
- Repeat previous step until there is only one possible jump spot or type <CR>
to jump to first available jump spot. Typing anything else stops jumping
without moving cursor.
# Setup ~
This module needs a setup with `require('mini.jump2d').setup({})` (replace
`{}` with your `config` table). It will create global Lua table
`MiniJump2d` which you can use for scripting or manually (with
`:lua MiniJump2d.*`).
See |MiniJump2d.config| for available config settings.
You can override runtime config settings locally to buffer inside
`vim.b.minijump2d_config` which should have same structure as
`MiniJump2d.config`. See |mini.nvim-buffer-local-config| for more details.
To stop module from showing non-error feedback, set `config.silent = true`.
# Example usage ~
- Modify default jumping to use only current window at or after cursor line: >lua
require('mini.jump2d').setup({
allowed_lines = { cursor_before = false },
allowed_windows = { not_current = false },
})
<
- Jump to word start using combination of options supplied in
|MiniJump2d.config| and |MiniJump2d.builtin_opts.line_start|: >vim
:lua MiniJump2d.start(MiniJump2d.builtin_opts.line_start)
<
- Jump to a single character typed after executing this command: >vim
:lua MiniJump2d.start(MiniJump2d.builtin_opts.single_character)
<
- See more examples in |MiniJump2d.start| and |MiniJump2d.builtin_opts|.
# Comparisons ~
- 'phaazon/hop.nvim':
- Both are fast, customizable, and extensible (user can write their own
ways to define jump spots).
- 'hop.nvim' visualizes all steps at once. While this module can show
configurable number of steps ahead.
- Both have several builtin ways to specify type of jump (word start,
line start, one character or query based on user input). 'hop.nvim'
does that by exporting many targeted Neovim commands, while this
module has preconfigured basic options leaving others to
customization with Lua code (see |MiniJump2d.builtin_opts|).
- 'hop.nvim' computes labels (called "hints") differently. Contrary to
this module deliberately not having preference of one jump spot over
another, 'hop.nvim' uses specialized algorithm that produces sequence
of keys in a slightly biased manner: some sequences are intentionally
shorter than the others (leading to fewer average keystrokes). They
are put near cursor (by default) and highlighted differently. Final
order of sequences is based on distance to the cursor.
- 'mini.jump2d' has opinionated default algorithm of computing jump
spots. See |MiniJump2d.default_spotter|.
# Highlight groups ~
* `MiniJump2dSpot` - highlighting of jump spot's next step. By default it
uses label with highest contrast while not being too visually demanding:
white on black for dark 'background', black on white for light. If it
doesn't suit your liking, try couple of these alternatives (or choose
your own, of course): >lua
-- Reverse underlying colors (mostly *very* visible in any colorscheme)
vim.api.nvim_set_hl(0, 'MiniJump2dSpot', { reverse = true })
-- Bold italic
vim.api.nvim_set_hl(0, 'MiniJump2dSpot', { bold = true, italic = true })
-- Red undercurl
vim.api.nvim_set_hl(0, 'MiniJump2dSpot', { sp = 'Red', undercurl = true })
<
* `MiniJump2dSpotUnique` - highlighting of jump spot's next step if it has
unique label. By default links to `MiniJump2dSpot`.
* `MiniJump2dSpotAhead` - highlighting of jump spot's future steps. By default
similar to `MiniJump2dSpot` but with less contrast and visibility.
* `MiniJump2dDim` - highlighting of lines with at least one jump spot.
Make it non-bright in order for jump spot labels to be more visible.
By default linked to `Comment` highlight group.
To change any highlight group, modify it directly with |:highlight|.
# Disabling ~
To disable, set `vim.g.minijump2d_disable` (globally) or
`vim.b.minijump2d_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.
------------------------------------------------------------------------------
*MiniJump2d.setup()*
`MiniJump2d.setup`({config})
Module setup
Parameters ~
{config} `(table|nil)` Module config table. See |MiniJump2d.config|.
Usage ~
>lua
require('mini.jump2d').setup() -- use default config
-- OR
require('mini.jump2d').setup({}) -- replace {} with your config table
<
------------------------------------------------------------------------------
*MiniJump2d.config*
`MiniJump2d.config`
Module config
Default values:
>lua
MiniJump2d.config = {
-- Function producing jump spots (byte indexed) for a particular line.
-- For more information see |MiniJump2d.start|.
-- If `nil` (default) - use |MiniJump2d.default_spotter|
spotter = nil,
-- Characters used for labels of jump spots (in supplied order)
labels = 'abcdefghijklmnopqrstuvwxyz',
-- Options for visual effects
view = {
-- Whether to dim lines with at least one jump spot
dim = false,
-- How many steps ahead to show. Set to big number to show all steps.
n_steps_ahead = 0,
},
-- Which lines are used for computing spots
allowed_lines = {
blank = true, -- Blank line (not sent to spotter even if `true`)
cursor_before = true, -- Lines before cursor line
cursor_at = true, -- Cursor line
cursor_after = true, -- Lines after cursor line
fold = true, -- Start of fold (not sent to spotter even if `true`)
},
-- Which windows from current tabpage are used for visible lines
allowed_windows = {
current = true,
not_current = true,
},
-- Functions to be executed at certain events
hooks = {
before_start = nil, -- Before jump start
after_jump = nil, -- After jump was actually done
},
-- Module mappings. Use `''` (empty string) to disable one.
mappings = {
start_jumping = '<CR>',
},
-- Whether to disable showing non-error feedback
silent = false,
}
<
# Options ~
## Spotter function ~
Actual computation of possible jump spots is done through spotter function.
It should have the following arguments:
- `line_num` is a line number inside buffer.
- `args` - table with additional arguments:
- {win_id} - identifier of a window where input line number is from.
- {win_id_init} - identifier of a window which was current when
`MiniJump2d.start()` was called.
Its output is a list of byte-indexed positions that should be considered as
possible jump spots for this particular line in this particular window.
Note: for a more aligned visualization this list should be (but not
strictly necessary) sorted increasingly.
Note: spotter function is always called with `win_id` window being
"temporary current" (see |nvim_win_call|). This allows using builtin
Vimscript functions that operate only inside current window.
## View ~
Option `view.n_steps_ahead` controls how many steps ahead to show along
with the currently required label. Those future steps are showed with
different (less visible) highlight group ("MiniJump2dSpotAhead"). Usually
it is a good idea to use this with a spotter which doesn't result into many
jump spots (like, for example, |MiniJump2d.builtin_opts.word_start|).
Default is 0 to not show anything ahead as it reduces visual noise.
Option `view.dim` controls whether to dim lines with at least one jump spot.
Dimming is done by applying "MiniJump2dDim" highlight group to the whole line.
## Allowed lines ~
Option `allowed_lines` controls which lines will be used for computing
possible jump spots:
- If `blank` or `fold` is `true`, it is possible to jump to first column of blank
line (determined by |prevnonblank|) or first folded one (determined by
|foldclosed|) respectively. Otherwise they are skipped. These lines are
not processed by spotter function even if the option is `true`.
- If `cursor_before`, (`cursor_at`, `cursor_after`) is `true`, lines before
(at, after) cursor line of all processed windows are forwarded to spotter
function. Otherwise, they don't. This allows control of jump "direction".
## Hooks ~
Following hook functions can be used to further tweak jumping experience:
- `before_start` - called without arguments first thing when jump starts.
One of the possible use cases is to ask for user input and update spotter
function with it.
- `after_jump` - called after jump was actually done. Useful to make
post-adjustments (like move cursor to first non-whitespace character).
------------------------------------------------------------------------------
*MiniJump2d.start()*
`MiniJump2d.start`({opts})
Start jumping
Compute possible jump spots, visualize them and wait for iterative filtering.
First computation of possible jump spots ~
- Process allowed windows (current and/or not current; controlled by
`allowed_windows` option) by visible lines from top to bottom. For each
one see if it is allowed (controlled by `allowed_lines` option). If not
allowed, then do nothing. If allowed and should be processed by
`spotter`, process it.
- Apply spotter function from `spotter` option for each appropriate line
and concatenate outputs. This means that eventual order of jump spots
aligns with lexicographical order within "window id" - "line number" -
"position in `spotter` output" tuples.
- For each possible jump compute its label: a single character from
`labels` option used to filter jump spots. Each possible label character
might be used more than once to label several "consecutive" jump spots.
It is done in an optimal way under assumption of no preference of one
spot over another. Basically, it means "use all labels at each step of
iterative filtering as equally as possible".
Visualization ~
Current label for each possible jump spot is shown at that position
overriding everything underneath it.
Iterative filtering ~
Labels of possible jump spots are computed in order to use them as equally
as possible.
Example:
- With `abc` as `labels` option, initial labels for 10 possible jumps
are "aaaabbbccc". As there are 10 spots which should be "coded" with 3
symbols, at least 2 symbols need 3 steps to filter them out. With current
implementation those are always the "first ones".
- After typing `a`, it filters first four jump spots and recomputes its
labels to be "aabc".
- After typing `a` again, it filters first two spots and recomputes its
labels to be "ab".
- After typing either `a` or `b` it filters single spot and makes jump.
With default 26 labels for most real-world cases 2 steps is enough for
default spotter function. Rarely 3 steps are needed with several windows.
Parameters ~
{opts} `(table|nil)` Configuration of jumping, overriding global and buffer
local values.config|. Has the same structure as |MiniJump2d.config|
without <mappings> field. Extra allowed fields:
- <hl_group> - which highlight group to use for first step.
Default: "MiniJump2dSpot".
- <hl_group_ahead> - which highlight group to use for second step and later.
Default: "MiniJump2dSpotAhead".
- <hl_group_dim> - which highlight group to use dimming used lines.
Default: "MiniJump2dSpotDim".
Usage ~
>lua
-- Start default jumping
MiniJump2d.start()
-- Jump to word start
MiniJump2d.start(MiniJump2d.builtin_opts.word_start)
-- Jump to single character from user input (follow by typing one character)
MiniJump2d.start(MiniJump2d.builtin_opts.single_character)
-- Jump to first character of punctuation group only inside current window
-- which is placed at cursor line; visualize with `Search`
MiniJump2d.start({
spotter = MiniJump2d.gen_pattern_spotter('%p+'),
allowed_lines = { cursor_before = false, cursor_after = false },
allowed_windows = { not_current = false },
hl_group = 'Search'
})
See also ~
|MiniJump2d.config|
------------------------------------------------------------------------------
*MiniJump2d.stop()*
`MiniJump2d.stop`()
Stop jumping
------------------------------------------------------------------------------
*MiniJump2d.gen_pattern_spotter()*
`MiniJump2d.gen_pattern_spotter`({pattern}, {side})
Generate spotter for Lua pattern
Parameters ~
{pattern} `(string|nil)` Lua pattern. Default: `'[^%s%p]+'` which matches group
of "non-whitespace non-punctuation characters" (basically a way of saying
"group of alphanumeric characters" that works with multibyte characters).
{side} `(string|nil)` Which side of pattern match should be considered as
jumping spot. Should be one of 'start' (start of match, default), 'end'
(inclusive end of match), or 'none' (match for spot is done manually
inside pattern with plain `()` matching group).
Return ~
`(function)` Spotter function.
Usage ~
>lua
-- Match any punctuation
MiniJump2d.gen_pattern_spotter('%p')
-- Match first from line start non-whitespace character
MiniJump2d.gen_pattern_spotter('^%s*%S', 'end')
-- Match start of last word
MiniJump2d.gen_pattern_spotter('[^%s%p]+[%s%p]-$', 'start')
-- Match letter followed by another letter (example of manual matching
-- inside pattern)
MiniJump2d.gen_pattern_spotter('%a()%a', 'none')
<
------------------------------------------------------------------------------
*MiniJump2d.gen_union_spotter()*
`MiniJump2d.gen_union_spotter`({...})
Generate union of spotters
Parameters ~
{...} `(any)` Each argument should be a valid spotter.
See |MiniJump2d.config| for more details.
Return ~
`(function)` Spotter producing union of spots.
Usage ~
>lua
-- Match start and end of non-blank character groups:
local nonblank_start = MiniJump2d.gen_pattern_spotter('%S+', 'start')
local nonblank_end = MiniJump2d.gen_pattern_spotter('%S+', 'end')
local spotter = MiniJump2d.gen_union_spotter(nonblank_start, nonblank_end)
<
------------------------------------------------------------------------------
*MiniJump2d.default_spotter*
`MiniJump2d.default_spotter`
Default spotter function
Spot is possible for jump if it is one of the following:
- Start or end of non-whitespace character group.
- Alphanumeric character followed or preceded by punctuation (useful for
snake case names).
- Start of uppercase character group (useful for camel case names). Usually
only Latin alphabet is recognized due to Lua patterns shortcomings.
These rules are derived in an attempt to balance between two intentions:
- Allow as much useful jumping spots as possible.
- Make labeled jump spots easily distinguishable.
Usually takes from 2 to 3 keystrokes to get to destination.
------------------------------------------------------------------------------
*MiniJump2d.builtin_opts*
`MiniJump2d.builtin_opts`
Table with builtin `opts` values for |MiniJump2d.start()|
Each element of table is itself a table defining one or several options for
`MiniJump2d.start()`. Read help description to see which options it defines
(like in |MiniJump2d.builtin_opts.line_start|).
Usage ~
>lua
-- Using `MiniJump2d.builtin_opts.line_start` as example:
-- Command
:lua MiniJump2d.start(MiniJump2d.builtin_opts.line_start)
-- Custom mapping
vim.keymap.set(
'n', '<CR>',
'<Cmd>lua MiniJump2d.start(MiniJump2d.builtin_opts.line_start)<CR>'
)
-- Inside `MiniJump2d.setup()` (make sure to use all defined options)
local jump2d = require('mini.jump2d')
local jump_line_start = jump2d.builtin_opts.line_start
jump2d.setup({
spotter = jump_line_start.spotter,
hooks = { after_jump = jump_line_start.hooks.after_jump }
})
<
------------------------------------------------------------------------------
*MiniJump2d.builtin_opts.default*
`MiniJump2d.builtin_opts.default`
Jump with |MiniJump2d.default_spotter()|
Defines `spotter`.
------------------------------------------------------------------------------
*MiniJump2d.builtin_opts.line_start*
`MiniJump2d.builtin_opts.line_start`
Jump to line start
Defines `spotter` and `hooks.after_jump`.
------------------------------------------------------------------------------
*MiniJump2d.builtin_opts.word_start*
`MiniJump2d.builtin_opts.word_start`
Jump to word start
Defines `spotter`.
------------------------------------------------------------------------------
*MiniJump2d.builtin_opts.single_character*
`MiniJump2d.builtin_opts.single_character`
Jump to single character taken from user input
Defines `spotter`, `allowed_lines.blank`, `allowed_lines.fold`, and
`hooks.before_start`.
------------------------------------------------------------------------------
*MiniJump2d.builtin_opts.query*
`MiniJump2d.builtin_opts.query`
Jump to query taken from user input
Defines `spotter`, `allowed_lines.blank`, `allowed_lines.fold`, and
`hooks.before_start`.
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -0,0 +1,725 @@
*mini.map* Window with buffer text overview
*MiniMap*
MIT License Copyright (c) 2022 Evgeni Chasnovski
==============================================================================
Features:
- Show and manage special floating window displaying automatically updated
overview of current buffer text. Window takes up whole height of Neovim
instance and is fixed to a left/right side. Map content is computed by
taking all current lines, converting it to binary whitespace/non-whitespace
mask, rescaling to appropriate dimensions, and converting back to strings
consisting from special encoding symbols. All this is done **very fast** and
**asynchronously**. See |MiniMap.open()|, |MiniMap.refresh()|, |MiniMap.close()|,
|MiniMap.toggle()|, |MiniMap.toggle_side()|.
For a general overview and tips, see |mini.map-usage|.
- Show scrollbar next to map content. It represents current line and view
(top and bottom visible lines). Can be the only thing shown, making map
window a "pure scrollbar". See "Pure scrollbar config" section in
|MiniMap.config|.
- Highlight map lines representing certain data in current buffer. This is
done via extensible set of callables, called integrations (see
"Integrations" section in |MiniMap.config|). There are pre-built generators
for common integrations:
- Builtin search (as result of |/| and similar).
- Builtin diagnostic (taken from |vim.diagnostic.get()|).
- General diff hunks provided by 'mini.diff'.
- Hunks computed provided by 'lewis6991/gitsigns.nvim'.
For more details see |MiniMap.gen_integration|.
- Focus on map window to quickly browse current (source) buffer. Moving inside
map window updates cursor position in source window enabling fast and
targeted buffer exploration. To focus back, hit `<CR>` to accept current
explored position or `<Esc>` to go back to original position. See
|MiniMap.toggle_focus()|.
- Customizable via |MiniMap.config| and/or `opts` argument of |MiniMap.open()|
or |MiniMap.refresh()|:
- Encoding symbols used to display binary information of different
resolution (default is 3x2). There are pre-built generators for
different basic character families and resolutions. See
|MiniMap.gen_encode_symbols|.
- Scrollbar symbols, separate for line and view. Can have any width
(even zero, which virtually disables scrollbar).
- Integrations producing map line highlights.
- Window options: side (left/right), width, 'winblend', and more.
What it doesn't do:
- Automatically refresh when typing in Insert mode. Although it can be done in
non-blocking way, it still might introduce considerable computation overhead
(especially in very large files).
- Has more flexible window configuration. In case a full height floating
window obstructs vision of underlying buffers, use |MiniMap.toggle()| or
|MiniMap.toggle_side()|. Works best with global statusline.
- Provide autoopen functionality. Due to vast differences in user preference
of when map window should be shown, set up of automatic opening is left to
user. A common approach would be to call `MiniMap.open()` on |VimEnter| event.
If you use |MiniStarter|, you can modify `<CR>` buffer mapping: >lua
local set_map_keymap = function()
local rhs = function()
MiniStarter.eval_current_item()
MiniMap.open()
end
vim.keymap.set('n', '<CR>', rhs, { buffer = true })
end
local opts = { pattern = 'MiniStarterOpened', callback = set_map_keymap }
vim.api.nvim_create_autocmd('User', opts)
<
# Setup ~
This module needs a setup with `require('mini.map').setup({})` (replace
`{}` with your `config` table). It will create global Lua table `MiniMap`
which you can use for scripting or manually (with `:lua MiniMap.*`).
See |MiniMap.config| for available config settings.
You can override runtime config settings (like `config.modifiers`) locally
to buffer inside `vim.b.minimap_config` which should have same structure
as `MiniMap.config`. See |mini.nvim-buffer-local-config| for more details.
# Dependencies ~
Suggested dependencies (provide extra functionality for integrations):
- Enabled 'mini.diff' module for general diff highlighting via
|MiniMap.gen_integration.diff()|. If missing, no highlighting is added.
- Plugin 'lewis6991/gitsigns.nvim' for Git status highlighting via
|MiniMap.gen_integration.gitsigns()|. If missing, no highlighting is added.
# Comparisons ~
- 'wfxr/minimap.vim':
- 'mini.map' doesn't have dependencies while being as fast as written
in Rust dependency of 'minimap.vim'.
- 'mini.map' uses floating window, while 'minimap.vim' uses regular one.
- 'mini.map' provides slightly different visual interface with
scrollbar and integration counts.
- 'mini.map' allows encode symbols customization, 'minimap.vim' does not.
- 'mini.map' allows extending highlight integrations, while only
builtin search and git status are supported in 'minimap.vim'.
- 'mini.map' updates in asynchronous (non-blocking) fashion, 'minimap.vim'
does not.
- 'mini.map' can be used as a pure scrollbar, 'minimap.vim' can not.
- 'dstein64/nvim-scrollview':
- 'mini.map' has two-part scrollbar showing current line and view (with
variable height), while 'nvim-scrollview' shows only current view
(with fixed height).
- 'nvim-scrollview' respects folds, i.e. shows view of visible lines,
while 'mini.map' by design always shows view based on actual lines.
- 'nvim-scrollview' creates scrollbar which can be dragged with mouse,
while 'mini.nvim' does not, by design (use |MiniMap.toggle_focus()|).
- 'mini.map' can show buffer outline, while 'nvim-scrollview' can not.
- 'mini.map' can show highlight integrations, while 'nvim-scrollview'
can not.
- 'petertriho/nvim-scrollbar':
- 'mini.map' has two-part scrollbar showing current line and view (with
variable height), while 'nvim-scrollbar' shows only current view.
- 'mini.map' can show buffer outline, while 'nvim-scrollbar' can not.
- 'mini.map' has fully extendable highlight integrations, while
'nvim-scrollbar' only supports diagnostic and search (with dependency).
- 'lewis6991/satellite.nvim':
- Almost the same differences as with 'dstein64/nvim-scrollview', except
'satellite.nvim' can display some set of integration highlights.
# Highlight groups ~
* `MiniMapNormal` - basic highlight of whole window.
* `MiniMapSymbolCount` - counts of per-line integration items.
* `MiniMapSymbolLine` - scrollbar part representing current line.
* `MiniMapSymbolView` - scrollbar part representing current view.
To change any highlight group, modify it directly with |:highlight|.
# Disabling ~
To disable, set `vim.g.minimap_disable` (globally) or `vim.b.minimap_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.
------------------------------------------------------------------------------
*mini.map-usage*
# Mappings ~
This module doesn't make mappings, only provides functions for users to map
manually. Here is how one |<Leader>| set of mappings can be constructed: >lua
vim.keymap.set('n', '<Leader>mc', MiniMap.close)
vim.keymap.set('n', '<Leader>mf', MiniMap.toggle_focus)
vim.keymap.set('n', '<Leader>mo', MiniMap.open)
vim.keymap.set('n', '<Leader>mr', MiniMap.refresh)
vim.keymap.set('n', '<Leader>ms', MiniMap.toggle_side)
vim.keymap.set('n', '<Leader>mt', MiniMap.toggle)
<
# How automatic refresh works ~
Automatic refresh is done by calling |MiniMap.refresh()| when appropriate
|events| occur. It is done with specially chosen `parts` argument value (to
avoid unnecessary computations). For example, when only cursor has moved
(|CursorMoved|), only scrollbar is updated; so no recomputation of integrations
or line encoding is done.
To avoid visual clutter, automatic refresh is done only in normal buffers
and help pages (i.e. with |buftype| being empty or "help")
When you think content is not up to date, try one of these:
- Call |MiniMap.refresh()| manually. Make mapping to make it easier.
- Save current buffer, for example with |:write|.
- Exit and enter Normal mode (if your Neovim version supports |ModeChanged|).
------------------------------------------------------------------------------
*MiniMap.setup()*
`MiniMap.setup`({config})
Module setup
Parameters ~
{config} `(table|nil)` Module config table. See |MiniMap.config|.
Usage ~
>lua
require('mini.map').setup() -- use default config
-- OR
require('mini.map').setup({}) -- replace {} with your config table
<
------------------------------------------------------------------------------
*MiniMap.config*
`MiniMap.config`
Module config
Default values:
>lua
MiniMap.config = {
-- Highlight integrations (none by default)
integrations = nil,
-- Symbols used to display data
symbols = {
-- Encode symbols. See `:h MiniMap.config` for specification and
-- `:h MiniMap.gen_encode_symbols` for pre-built ones.
-- Default: solid blocks with 3x2 resolution.
encode = nil,
-- Scrollbar parts for view and line. Use empty string to disable any.
scroll_line = '█',
scroll_view = '┃',
},
-- Window options
window = {
-- Whether window is focusable in normal way (with `wincmd` or mouse)
focusable = false,
-- Side to stick ('left' or 'right')
side = 'right',
-- Whether to show count of multiple integration highlights
show_integration_count = true,
-- Total width
width = 10,
-- Value of 'winblend' option
winblend = 25,
-- Z-index
zindex = 10,
},
}
<
# Options ~
## Symbols ~
Options in `config.symbols` define characters used to display various
information in map window.
### Encode symbols ~
The `config.symbols.encode` option defines which characters are used to
encode source buffer lines. For details of encode algorithm, see
|MiniMap.encode_strings()|.
This option should be a table with the following structure:
- <resolution> field - table containing <row> and <col> elements with row
and column resolution of each symbol. This defines encoding structure and
number of needed encode symbols.
- Numerical fields 1, 2, ..., 2^(row_resolution * col_resolution). Each symbol
represents a `(row_resolution, col_resolution)` boolean mask (`true` for
non-whitespace, `false` for whitespace), created as (reversed) binary digit:
`true` as 1; `false` as 0. Traversing left-right, top-bottom (top-left is
lowest bit, bottom-right - highest). So first symbol encodes a complete
whitespace, last - complete non-whitespace.
If `nil` (default), output of |MiniMap.gen_encode_symbols.block()| with `'3x2'`
identifier is used.
Example: { '1', '2', '3', '4', resolution = { row = 1, col = 2 } }. This
will encode two characters in each input row. So a string `' a aaa'` will
be encoded as `'1234'`.
There are pre-built generators of encode symbols:
- |MiniMap.gen_encode_symbols.block()|
- |MiniMap.gen_encode_symbols.dot()|
- |MiniMap.gen_encode_symbols.shade()|
### Scrollbar symbols ~
Options `config.symbols.scroll_line` and `config.symbols.scroll_view` define
strings used to represent current line and current view inside map window.
Can have any length, map window content will adjust.
If supplied window width is small enough so that only (part of) of
scrollbar can be shown, it is called a "pure scrollbar". The behavior differs
slightly from normal map window. See "Pure scrollbar config" later section.
Some suggestions for scrollbar symbols:
- View-line pairs: '▒' and '█'.
- Line - '🮚', '▶'.
- View - '╎', '┋', '┋'.
## Integrations ~
Option `config.integrations` is an array of integrations. Each one is used
to define map line highlights representing some important lines in source
buffer. If `nil` (default), no integrations are used.
Each integration should be a callable returning an array with data about
**source buffer** lines it wants to highlight. Each array element should be
a table with <line> (source buffer line number) and <hl_group> (string with
highlight group name) keys. Note: line number outside of source buffer
count will be converted to a nearest appropriate one.
Example output of single integration: >lua
{
{ line = 1, hl_group = 'Search' },
{ line = 2, hl_group = 'Operator' },
{ line = 9, hl_group = 'Search'}
}
<
Conversion to map highlights is done on a "first seen" basis: actual
highlight group applied to a map line is taken from the first integration
output convertible to that map line. Other outputs with same map line
(after conversion) contribute to integration count shown between scrollbar
and encoded lines (if `config.window.show_integration_count` is `true`).
Previous example output with default `'3x2'` resolution will add |hl-Search|
highlight on map lines 1 and 3, and show integration count 2 on first line.
Every element of integrations array is called one by one from start to end
with their outputs appended to end of single array. This means that more
important integrations should be placed in the beginning of an array, as
this will make them have higher priority in case other integrations will
highlight same map line.
Example of using `config.integrations`: >lua
local map = require('mini.map')
map.setup({
integrations = {
map.gen_integration.builtin_search(),
map.gen_integration.diff(),
map.gen_integration.diagnostic(),
},
})
<
## Window config ~
Option `config.window` defines some properties of map window.
`window.focusable` - whether to allow focusing on map window with other
methods beside |MiniMap.toggle_focus()| (like |wincmd|, |CTRL-W|, or
mouse). Default: `false`.
`window.side` - which side to stick map window: `'left'` or `'right'` (default).
`window.show_integration_count` - whether to show integration count between
scrollbar and encoded lines. Integration count is a number of integration
outputs which were converted to same map line. When `true`, adds single
cell column with numbers from 2 to 9 and character '+' indicating count
greater than 9. Count 1 is not shown, because it is redundant to highlighted
map line. Default: `true`.
`window.width` - width of floating window, including scrollbar and
integration count column. Default: 10.
`window.winblend` - value of 'winblend' of floating window. Value 0 makes it
completely non-transparent, 100 - completely transparent (content is still
visible, but with slightly different highlights).
`window.zindex` - z-index of floating window. Default: 10.
# Pure scrollbar config ~
"Pure scrollbar" is a configuration when window width is not enough to show
encoded content. It has following differences from default "map" approach:
- It doesn't perform line encoding with |MiniMap.encode_strings()|
but instead uses encoding with fixed number of lines (equal to window
height).
- Integration highlights are not computed.
Config: >lua
require('mini.map').setup({
-- Customize `symbols` to your liking
window = {
-- Set this to the maximum width of your scroll symbols
width = 1,
-- Set this to your liking. Try values 0, 25, 50, 75, 100
winblend = 100,
-- Don't need extra column
show_integration_count = false,
}
})
<
------------------------------------------------------------------------------
*MiniMap.current*
`MiniMap.current`
Table with information about current state of map
At least these keys are supported:
- <buf_data> - table with buffer identifiers. Field <map> contains
identifier of a buffer used to display map. Field <source> - buffer
identifier which content map is displaying (i.e. source buffer).
- <win_data> - table of window identifiers used to display map in certain
tabpage. Keys: tabpage identifier. Values: window identifier.
- <opts> - current options used to control map display. Same structure
as |MiniMap.config|. Takes precedence over global and buffer-local configs.
Is reset when last map window is closed with |MiniMap.close()|.
------------------------------------------------------------------------------
*MiniMap.encode_strings()*
`MiniMap.encode_strings`({strings}, {opts})
Encode strings
This takes arbitrary array of strings and computes its non-whitespace
outline. Output is an array of strings with configurable array length, string
width, and symbols representing encoding.
Each encode symbol is assumed to have resolution within which it can convey
binary information. For example, resolution `3x2` (row resolution 3,
column - 2) means that each symbol can encode 3 rows and 2 columns of
binary data. Here it is used to encode non-whitespace mask. See more in
"Encode symbols" section of |MiniMap.config|.
Encoding has the following steps:
- Convert strings to boolean mask: 2d boolean array with each row
representing a string. Element in every row subarray is `true` if
respective (possibly multibyte) character in a string is not a whitespace,
`false` otherwise. Note: tabs are expanded into 'tabstop' spaces.
- Rescale to appropriate dimensions:
- Each output dimension is just enough to encode all input strings, but
not more than supplied dimensions (`opts.n_rows * resolution.row` and
`opts.n_cols * resolution.col` respectively).
- If input dimensions are too big to fit inside output, perform grid
downscaling with loss of information. Input boolean mask is divided
into 2d-bins with as equal as possible dimensions. Each bin then
converted into single boolean value: `true` if bin contains at least
one `true` element, `false` otherwise. This leads to a whitespace
output meaning that **all** entries in a bin are whitespace, while
non-whitespace output means that **some** entry is non-whitespace.
- Convert boolean mask to symbol strings:
- Input rescaled boolean mask is divided into bins with dimensions of
symbol resolution (assuming `false` outer padding).
- Each bin with resolution dimensions is transformed into encode symbol.
Single convertible `(resolution.row, resolution.col)` boolean
mask is treated as (reversed) binary digit: `true` as 1; `false` as 0.
Traversing left-right, top-bottom (top-left is lowest bit,
bottom-right - highest).
Example ~
Assume the output should have 3 rows of symbols each with width 2. Encode
symbols are ' ', '▌', '▐', '█' with `1x2` resolution.
Assume input strings: >
aaaaa
b b
d d
e e
<
Steps:
- Convert to boolean mask (each row is a boolean array, "t"/"f" ~ `true`/`false`,
empty spots are equivalent to being `false`): >
ttttt
ftft
ftft
tft
<
- Rescale. Output dimensions are `n_rows * resolution.row = 3 * 1 = 3` rows and
`n_cols * resolution.col = 2 * 2 = 4`. It creates as equal as possible grid
with 3 rows and 4 columns and converts bins to single booleans. Result: >
tttt
tftf
ttff
<
- Convert to symbols. It makes `1x2` bins, treats their input as (reversed)
binary digits (`ff=00=0`, `tf=10=1`, `ft=01=2`, `tt=11=3`) and takes
corresponding symbols from supplied options (value plus 1). Result: >
██
▌▌
<
Parameters ~
{strings} `(table)` Array of arbitrary strings.
{opts} `(table|nil)` Options. Possible fields:
- <n_rows> - number of rows in output encoding. If too big, will be
truncated to be maximum needed to encode all input strings (taking into
account symbols row resolution). Default: `math.huge`.
- <n_cols> - width of every encoding string. If too big, will be truncated
to be maximum needed to encode all input strings (taking into account
symbols column resolution). Default: `math.huge`.
- <symbols> - array of symbols with extra `resolution` field. See "Encode
symbols" section of |MiniMap.config| for more details. Default: output
of |MiniMap.gen_encode_symbols.block()| with `'3x2'` identifier.
Return ~
`(table)` Array of encoded strings.
------------------------------------------------------------------------------
*MiniMap.open()*
`MiniMap.open`({opts})
Open map window
This creates and shows map window in current tabpage. It basically has
two steps:
- If not already done, create map buffer (used to set lines and other
visual indicators) and map window.
- Call |MiniMap.refresh()|.
Parameters ~
{opts} `(table|nil)` Options used to define map configuration. Same structure
as |MiniMap.config|. Will have effect until at least one tabpage has opened
map window. Default values are taken in the following order:
- From `opts` field of |MiniMap.current|.
- From `vim.b.minimap_config`.
- From |MiniMap.config|.
------------------------------------------------------------------------------
*MiniMap.refresh()*
`MiniMap.refresh`({opts}, {parts})
Refresh map window
This function serves two purposes:
- Update current map configuration via `opts`.
- Update parts of displayed content via `parts`.
Parameters ~
{opts} `(table|nil)` Options used to define map configuration. Same structure
as |MiniMap.config|. Will have effect until at least one tabpage has opened
map window. Default values are taken in the following order:
- From `opts` field of |MiniMap.current|.
- From `vim.b.minimap_config`.
- From |MiniMap.config|.
{parts} `(table|nil)` Which parts to update. Recognised keys with boolean
values (all `true` by default):
- <integrations> - whether to update integration highlights.
- <lines> - whether to update map lines.
- <scrollbar> - whether to update scrollbar.
------------------------------------------------------------------------------
*MiniMap.close()*
`MiniMap.close`()
Close map window
Also resets `opts` field of |MiniMap.current| after closing last map window
(among possibly several tabpages).
------------------------------------------------------------------------------
*MiniMap.toggle()*
`MiniMap.toggle`({opts})
Toggle map window
Open if not shown in current tabpage, close otherwise.
Parameters ~
{opts} `(table|nil)` Input for |MiniMap.open()|.
------------------------------------------------------------------------------
*MiniMap.toggle_focus()*
`MiniMap.toggle_focus`({use_previous_cursor})
Toggle focus to/from map window
When not inside map window, put cursor inside map window; otherwise put
cursor in previous window with source buffer.
When cursor is moving inside map window (but not just after focusing), view of
source window is updated to show first line convertible to current map line.
This allows quick targeted source buffer exploration.
There are at least these extra methods to focus back from map window:
- Press `<CR>` to accept current explored position in source buffer.
Equivalent to calling this function with `false` argument.
- Press `<Esc>` to go back to original position prior focusing on map window.
Equivalent to calling this function with `true` argument.
Parameters ~
{use_previous_cursor} `(boolean|nil)` Whether to focus on source window at
original cursor position (the one prior focusing on map window).
------------------------------------------------------------------------------
*MiniMap.toggle_side()*
`MiniMap.toggle_side`()
Toggle side of map window
A small convenience wrapper for calling |MiniMap.refresh()| to change the
side of map window.
------------------------------------------------------------------------------
*MiniMap.gen_encode_symbols*
`MiniMap.gen_encode_symbols`
Generate encode symbols
This is a table with function elements. Call to actually get encode symbols.
Each element takes a string resolution identifier of a form `'rxc'` (like `'3x2'`)
where `r` is a row resolution of each symbol (how many rows of binary data it
can encode) and `c` is a column resolution (how many columns it can encode).
------------------------------------------------------------------------------
*MiniMap.gen_encode_symbols.block()*
`MiniMap.gen_encode_symbols.block`({id})
Generate block encode symbols
Outputs use solid block to encode binary data. Example: '🬗', '▟', '█'.
Parameters ~
{id} `(string)` Resolution identifier.
Available values: `'1x2'`, `'2x1'`, `'2x2'`, `'3x2'` (default in 'mini.map').
------------------------------------------------------------------------------
*MiniMap.gen_encode_symbols.dot()*
`MiniMap.gen_encode_symbols.dot`({id})
Generate dot encode symbols
Outputs use dots to encode binary data. Example: '⡪', '⣼', '⣿'.
Parameters ~
{id} `(string)` Resolution identifier. Available values: `'4x2'`, `'3x2'`.
------------------------------------------------------------------------------
*MiniMap.gen_encode_symbols.shade()*
`MiniMap.gen_encode_symbols.shade`({id})
Generate shade encode symbols
Outputs use whole cell shades to encode binary data. They use same set of
characters ('░', '▒', '▒', '▓), but with different resolution.
Parameters ~
{id} `(string)` Resolution identifier. Available values: `'1x2'`, `'2x1'`.
------------------------------------------------------------------------------
*MiniMap.gen_integration*
`MiniMap.gen_integration`
Generate integrations
This is a table with function elements. Call to actually get encode symbols.
Each element takes a table defining highlight groups used for to highlight
map lines.
------------------------------------------------------------------------------
*MiniMap.gen_integration.builtin_search()*
`MiniMap.gen_integration.builtin_search`({hl_groups})
Builtin search
Highlight lines with matches of current builtin search (like with |/|, |?|, etc.).
Integration count reflects number of actual matches.
It prompts integration highlighting update on every change of |hlsearch| option
(see |OptionSet|). Note that it is not happening for some keys:
- Toggle search highlight with |CTRL-L-default| or `\h` from 'mini.basics'.
Use custom mapping which changes mode. Like this: >lua
vim.keymap.set('n', [[\h]], ':let v:hlsearch = 1 - v:hlsearch<CR>')
<
- After starting search with |n|, |N|, |star|, or |#|.
To enable highlight update on this keys, make custom mappings. Like this: >lua
for _, key in ipairs({ 'n', 'N', '*', '#' }) do
local rhs = key ..
'<Cmd>lua MiniMap.refresh({}, {lines = false, scrollbar = false})<CR>'
vim.keymap.set('n', key, rhs)
end
<
Parameters ~
{hl_groups} `(table|nil)` Table defining highlight groups. Can have the
following fields:
- <search> - highlight group for search matches. Default: |hl-Search|.
------------------------------------------------------------------------------
*MiniMap.gen_integration.diagnostic()*
`MiniMap.gen_integration.diagnostic`({hl_groups})
Builtin diagnostic
Highlight lines with matches of current diagnostic items. Items are computed
with |vim.diagnostic.get()| for current (source) buffer.
It prompts integration highlighting update on every |DiagnosticChanged| event.
Diagnostic items with higher severity (see |vim.diagnostic.severity|) have
higher highlight priority (errors will be shown over all others, etc.).
Parameters ~
{hl_groups} `(table|nil)` Table defining highlight groups. Supplied fields
also define which diagnostic severity to highlight.
Can have the following fields:
- <error> - highlight group for error items.
Default: |hl-DiagnosticFloatingError|.
- <warn> - highlight group for warning items. Default: `nil` (not shown).
- <info> - highlight group for info items. Default: `nil` (not shown).
- <hint> - highlight group for hint items. Default: `nil` (not shown).
Usage ~
>lua
-- Show all diagnostic levels
local map = require('mini.map')
local diagnostic_integration = map.gen_integration.diagnostic({
error = 'DiagnosticFloatingError',
warn = 'DiagnosticFloatingWarn',
info = 'DiagnosticFloatingInfo',
hint = 'DiagnosticFloatingHint',
})
map.setup({ integrations = { diagnostic_integration } })
<
------------------------------------------------------------------------------
*MiniMap.gen_integration.diff()*
`MiniMap.gen_integration.diff`({hl_groups})
General diff hunks from 'mini.diff'
Highlight lines which are part of current diff.
Requires 'mini.diff' as dependency.
Parameters ~
{hl_groups} `(table|nil)` Table defining highlight groups. If `nil` (not
supplied), this status is not highlighted. Can have the following fields:
- <add> - group name for "add" hunks. Default: "MiniDiffSignAdd".
- <change> - group name for "change" hunks. Default: "MiniDiffSignChange".
- <delete> - group name for "delete" hunks. Default: "MiniDiffSignDelete".
------------------------------------------------------------------------------
*MiniMap.gen_integration.gitsigns()*
`MiniMap.gen_integration.gitsigns`({hl_groups})
Hunks from 'lewis6991/gitsigns.nvim'
Highlight lines which have non-trivial Git status.
Requires 'lewis6991/gitsigns.nvim' dependency.
Parameters ~
{hl_groups} `(table|nil)` Table defining highlight groups. If `nil` (not
supplied), this status is not highlighted. Can have the following fields:
- <add> - group name for added lines. Default: "GitSignsAdd".
- <change> - group name for changed lines. Default: "GitSignsChange".
- <delete> - group name for deleted lines. Default: "GitSignsDelete".
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -0,0 +1,305 @@
*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.
------------------------------------------------------------------------------
*MiniMisc.setup()*
`MiniMisc.setup`({config})
Module setup
Parameters ~
{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.config*
`MiniMisc.config`
Module config
Default values:
>lua
MiniMisc.config = {
-- Array of fields to make global (to be used as independent variables)
make_global = { 'put', 'put_text' },
}
<
------------------------------------------------------------------------------
*MiniMisc.bench_time()*
`MiniMisc.bench_time`({f}, {n}, {...})
Execute `f` several times and time how long it took
Parameters ~
{f} `(function)` Function which execution to benchmark.
{n} `(number|nil)` Number of times to execute `f(...)`. Default: 1.
{...} `(any)` Arguments when calling `f`.
Return ~
`(...)` Table with durations (in seconds; up to nanoseconds) and
output of (last) function execution.
------------------------------------------------------------------------------
*MiniMisc.get_gutter_width()*
`MiniMisc.get_gutter_width`({win_id})
Compute width of gutter (info column on the left of the window)
Parameters ~
{win_id} `(number|nil)` Window identifier (see |win_getid()|) for which gutter
width is computed. Default: 0 for current.
------------------------------------------------------------------------------
*MiniMisc.put()*
`MiniMisc.put`({...})
Print Lua objects in command line
Parameters ~
{...} `(any)` Any number of objects to be printed each on separate line.
------------------------------------------------------------------------------
*MiniMisc.put_text()*
`MiniMisc.put_text`({...})
Print Lua objects in current buffer
Parameters ~
{...} `(any)` Any number of objects to be printed each on separate line.
------------------------------------------------------------------------------
*MiniMisc.resize_window()*
`MiniMisc.resize_window`({win_id}, {text_width})
Resize window to have exact number of editable columns
Parameters ~
{win_id} `(number|nil)` Window identifier (see |win_getid()|) to be resized.
Default: 0 for current.
{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.setup_auto_root()*
`MiniMisc.setup_auto_root`({names}, {fallback})
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`.
Parameters ~
{names} `(table|function|nil)` Forwarded to |MiniMisc.find_root()|.
{fallback} `(function|nil)` Forwarded to |MiniMisc.find_root()|.
Usage ~
>lua
require('mini.misc').setup()
MiniMisc.setup_auto_root()
<
------------------------------------------------------------------------------
*MiniMisc.find_root()*
`MiniMisc.find_root`({buf_id}, {names}, {fallback})
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.
Parameters ~
{buf_id} `(number|nil)` Buffer identifier (see |bufnr()|) to use.
Default: 0 for current.
{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' }`.
{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.setup_termbg_sync()*
`MiniMisc.setup_termbg_sync`()
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_restore_cursor()*
`MiniMisc.setup_restore_cursor`({opts})
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.
Parameters ~
{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.stat_summary()*
`MiniMisc.stat_summary`({t})
Compute summary statistics of numerical array
This might be useful to compute summary of time benchmarking with
|MiniMisc.bench_time|.
Parameters ~
{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.tbl_head()*
`MiniMisc.tbl_head`({t}, {n})
Return "first" elements of table as decided by `pairs`
Note: order of elements might vary.
Parameters ~
{t} `(table)` Input table.
{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_tail()*
`MiniMisc.tbl_tail`({t}, {n})
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.
Parameters ~
{t} `(table)` Input table.
{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.use_nested_comments()*
`MiniMisc.use_nested_comments`({buf_id})
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.
Parameters ~
{buf_id} `(number|nil)` Buffer identifier (see |bufnr()|) in which function
will operate. Default: 0 for current.
------------------------------------------------------------------------------
*MiniMisc.zoom()*
`MiniMisc.zoom`({buf_id}, {config})
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.
Parameters ~
{buf_id} `(number|nil)` Buffer identifier (see |bufnr()|) to be zoomed.
Default: 0 for current.
{config} `(table|nil)` Optional config for window (as for |nvim_open_win()|).
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -0,0 +1,184 @@
*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.
------------------------------------------------------------------------------
*MiniMove.setup()*
`MiniMove.setup`({config})
Module setup
Parameters ~
{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.config*
`MiniMove.config`
Module config
Default values:
>lua
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,
},
}
<
# 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.move_selection()*
`MiniMove.move_selection`({direction}, {opts})
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_>|.
Parameters ~
{direction} `(string)` One of "left", "down", "up", "right".
{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|.
------------------------------------------------------------------------------
*MiniMove.move_line()*
`MiniMove.move_line`({direction}, {opts})
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).
Parameters ~
{direction} `(string)` One of "left", "down", "up", "right".
{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|.
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -0,0 +1,392 @@
*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.
------------------------------------------------------------------------------
*MiniNotify-specification*
# 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.
------------------------------------------------------------------------------
*MiniNotify.setup()*
`MiniNotify.setup`({config})
Module setup
Parameters ~
{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.config*
`MiniNotify.config`
Module config
Default values:
>lua
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,
},
}
<
# 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.make_notify()*
`MiniNotify.make_notify`({opts})
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)
<
Parameters ~
{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.add()*
`MiniNotify.add`({msg}, {level}, {hl_group})
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)
<
Parameters ~
{msg} `(string)` Notification message.
{level} `(string|nil)` Notification level as key of |vim.log.levels|.
Default: `'INFO'`.
{hl_group} `(string|nil)` Notification highlight group.
Default: `'MiniNotifyNormal'`.
Return ~
`(number)` Notification identifier.
------------------------------------------------------------------------------
*MiniNotify.update()*
`MiniNotify.update`({id}, {new_data})
Update active notification
Modify data of active notification.
Parameters ~
{id} `(number)` Identifier of currently active notification as returned
by |MiniNotify.add()|.
{new_data} `(table)` Table with data to update. Keys should be as non-timestamp
fields of |MiniNotify-specification| and values - new notification values.
------------------------------------------------------------------------------
*MiniNotify.remove()*
`MiniNotify.remove`({id})
Remove notification
If notification is active, make it not active (by setting `ts_remove` field).
If not active, do nothing.
Parameters ~
{id} `(number|nil)` Identifier of previously added notification.
If it is not, nothing is done (silently).
------------------------------------------------------------------------------
*MiniNotify.clear()*
`MiniNotify.clear`()
Remove all active notifications
Hide all active notifications and stop showing window (if shown).
------------------------------------------------------------------------------
*MiniNotify.refresh()*
`MiniNotify.refresh`()
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.get()*
`MiniNotify.get`({id})
Get previously added notification by id
Parameters ~
{id} `(number)` Identifier of notification.
Return ~
`(table)` Notification object (see |MiniNotify-specification|).
------------------------------------------------------------------------------
*MiniNotify.get_all()*
`MiniNotify.get_all`()
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.show_history()*
`MiniNotify.show_history`()
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.default_format()*
`MiniNotify.default_format`({notif})
Default content format
Used by default as `config.content.format`. Prepends notification message
with the human readable update time and a separator.
Parameters ~
{notif} `(table)` Notification object (see |MiniNotify-specification|).
Return ~
`(string)` Formatted notification message.
------------------------------------------------------------------------------
*MiniNotify.default_sort()*
`MiniNotify.default_sort`({notif_arr})
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).
Parameters ~
{notif_arr} `(table)` Array of notifications (see |MiniNotify-specification|).
Return ~
`(table)` Sorted array of notifications.
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -0,0 +1,472 @@
*mini.operators* Text edit operators
*MiniOperators*
MIT License Copyright (c) 2023 Evgeni Chasnovski
==============================================================================
Features:
- Operators:
- Evaluate text and replace with output.
- Exchange text regions.
- Multiply (duplicate) text.
- Replace text with register.
- Sort text.
- Automated configurable mappings to operate on textobject, line, selection.
Can be disabled in favor of more control with |MiniOperators.make_mappings()|.
- All operators support |[count]| and dot-repeat.
See |MiniOperators-overview| and |MiniOperators.config| for more details.
# Setup ~
This module needs a setup with `require('mini.operators').setup({})` (replace
`{}` with your `config` table). It will create global Lua table `MiniOperators`
which you can use for scripting or manually (with `:lua MiniOperators.*`).
See |MiniOperators.config| for available config settings.
You can override runtime config settings (but not `config.mappings`) locally
to buffer inside `vim.b.minioperators_config` which should have same structure
as `MiniOperators.config`. See |mini.nvim-buffer-local-config| for more details.
# Comparisons ~
- 'gbprod/substitute.nvim':
- Has "replace" and "exchange" variants, but not others from this module.
- Has "replace/substitute" over range functionality, while this module
does not by design (it is similar to |:s| functionality while not
offering significantly lower mental complexity).
- "Replace" highlights pasted text, while in this module it doesn't.
- "Exchange" doesn't work across buffers, while in this module it does.
- 'svermeulen/vim-subversive':
- Main inspiration for "replace" functionality, so they are mostly similar
for this operator.
- Has "replace/substitute" over range functionality, while this module
does not by design.
- 'tommcdo/vim-exchange':
- Main inspiration for "exchange" functionality, so they are mostly
similar for this operator.
- Doesn't work across buffers, while this module does.
- 'christoomey/vim-sort-motion':
- Uses |:sort| for linewise sorting, while this module uses consistent
sorting algorithm (by default, see |MiniOperators.default_sort_func()|).
- Sorting algorithm can't be customized, while this module allows this
(see `sort.func` in |MiniOperators.config|).
- For charwise region uses only commas as separators, while this module
can also separate by semicolon or whitespace (by default,
see |MiniOperators.default_sort_func()|).
# Highlight groups ~
* `MiniOperatorsExchangeFrom` - first region to exchange.
To change any highlight group, modify it directly with |:highlight|.
# Disabling ~
To disable main functionality, set `vim.g.minioperators_disable` (globally) or
`vim.b.minioperators_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.
------------------------------------------------------------------------------
*MiniOperators-overview*
# General overview ~
Operator defines an action that will be performed on a textobject, motion,
or visual selection (similar to |d|, |c|, etc.). When makes sense, it can also
respect supplied register (like "replace" operator).
This module implements each operator in a separate dedicated function
(like |MiniOperators.replace()| for "replace" operator). Each such function
takes `mode` as argument and acts depending on it:
- If `mode` is `nil` (or not explicitly supplied), it sets |operatorfunc|
to this dedicated function and returns `g@` assuming being called from
expression mapping. See |:map-operator| and |:map-expression| for more details.
- If `mode` is "char", "line", or "block", it acts as `operatorfunc` and performs
action for region between |`[| and |`]| marks.
- If `mode` is "visual", it performs action for region between |`<| and |`>| marks.
For more details about specific operator, see help for its function:
- Evaluate: |MiniOperators.evaluate()|
- Exchange: |MiniOperators.exchange()|
- Multiply: |MiniOperators.multiply()|
- Replace: |MiniOperators.replace()|
- Sort: |MiniOperators.sort()|
*MiniOperators-mappings*
## Mappings ~
All operators are automatically mapped during |MiniOperators.setup()| execution.
Mappings keys are deduced from `prefix` field of corresponding `config` entry.
All built-in conflicting mappings are removed (like |gra|, |grn| in Neovim>=0.11).
For each operator the following mappings are created:
- In Normal mode to operate on textobject. Uses `prefix` directly.
- In Normal mode to operate on line. Appends to `prefix` the last character.
This aligns with |operator-doubled| and established patterns for operators
with more than two characters, like |guu|, |gUU|, etc.
- In Visual mode to operate on visual selection. Uses `prefix` directly.
Example of default mappings for "replace":
- `gr` in Normal mode for operating on textobject.
Example of usage: `griw` replaces "inner word" with default register.
- `grr` in Normal mode for operating on line.
Example of usage: `grr` replaces current line.
- `gr` in Visual mode for operating on visual selection.
Example of usage: `viw` selects "inner word" and `gr` replaces it.
There are two suggested ways to customize mappings:
- Change `prefix` in |MiniOperators.setup()| call. For example, doing >lua
require('mini.operators').setup({ replace = { prefix = 'cr' } })
<
will make mappings for `cr` / `crr` / `cr` instead of `gr` / `grr` / `gr`.
- Disable automated mapping creation by supplying empty string as prefix and
use |MiniOperators.make_mappings()| directly. For example: >lua
-- Disable automated creation of "replace"
local operators = require('mini.operators')
operators.setup({ replace = { prefix = '' } })
-- Make custom mappings
operators.make_mappings(
'replace',
{ textobject = 'cr', line = 'crr', selection = 'cr' }
)
<
------------------------------------------------------------------------------
*MiniOperators.setup()*
`MiniOperators.setup`({config})
Module setup
Parameters ~
{config} `(table|nil)` Module config table. See |MiniOperators.config|.
Usage ~
>lua
require('mini.operators').setup() -- use default config
-- OR
require('mini.operators').setup({}) -- replace {} with your config table
<
------------------------------------------------------------------------------
*MiniOperators.config*
`MiniOperators.config`
Module config
Default values:
>lua
MiniOperators.config = {
-- Each entry configures one operator.
-- `prefix` defines keys mapped during `setup()`: in Normal mode
-- to operate on textobject and line, in Visual - on selection.
-- Evaluate text and replace with output
evaluate = {
prefix = 'g=',
-- Function which does the evaluation
func = nil,
},
-- Exchange text regions
exchange = {
prefix = 'gx',
-- Whether to reindent new text to match previous indent
reindent_linewise = true,
},
-- Multiply (duplicate) text
multiply = {
prefix = 'gm',
-- Function which can modify text before multiplying
func = nil,
},
-- Replace text with register
replace = {
prefix = 'gr',
-- Whether to reindent new text to match previous indent
reindent_linewise = true,
},
-- Sort text
sort = {
prefix = 'gs',
-- Function which does the sort
func = nil,
}
}
<
# Evaluate ~
`evaluate.prefix` is a string used to automatically infer operator mappings keys
during |MiniOperators.setup()|. See |MiniOperators-mappings|.
`evaluate.func` is a function used to actually evaluate text region.
If `nil` (default), |MiniOperators.default_evaluate_func()| is used.
This function will take content table representing selected text as input
and should return array of lines as output (each item per line).
Content table has fields `lines`, array of region lines, and `submode`,
one of `v`, `V`, `\22` (escaped `<C-v>`) for charwise, linewise, and blockwise.
To customize evaluation per language, set `evaluate.func` in buffer-local
config (`vim.b.minioperators_config`; see |mini.nvim-buffer-local-config|).
# Exchange ~
`exchange.prefix` is a string used to automatically infer operator mappings keys
during |MiniOperators.setup()|. See |MiniOperators-mappings|.
Note: default value "gx" overrides |netrw-gx| and |gx| / |v_gx|. If you prefer
using its original functionality, choose different `config.prefix`.
`exchange.reindent_linewise` is a boolean indicating whether newly put linewise
text should preserve indent of replaced text. In other words, if `false`,
regions are exchanged preserving their indents; if `true` - without them.
# Multiply ~
`multiply.prefix` is a string used to automatically infer operator mappings keys
during |MiniOperators.setup()|. See |MiniOperators-mappings|.
`multiply.func` is a function used to optionally update multiplied text.
If `nil` (default), text used as is.
Takes content table as input (see "Evaluate" section) and should return
array of lines as output.
# Replace ~
`replace.prefix` is a string used to automatically infer operator mappings keys
during |MiniOperators.setup()|. See |MiniOperators-mappings|.
`replace.reindent_linewise` is a boolean indicating whether newly put linewise
text should preserve indent of replaced text.
# Sort ~
`sort.prefix` is a string used to automatically infer operator mappings keys
during |MiniOperators.setup()|. See |MiniOperators-mappings|.
`sort.func` is a function used to actually sort text region.
If `nil` (default), |MiniOperators.default_sort_func()| is used.
Takes content table as input (see "Evaluate" section) and should return
array of lines as output.
Example of `sort.func` which asks user for custom delimiter for charwise region: >lua
local sort_func = function(content)
local opts = {}
if content.submode == 'v' then
-- Ask for delimiter to be treated as is (not as Lua pattern)
local delimiter = vim.fn.input('Sort delimiter: ')
-- Treat surrounding whitespace as part of split
opts.split_patterns = { '%s*' .. vim.pesc(delimiter) .. '%s*' }
end
return MiniOperators.default_sort_func(content, opts)
end
require('mini.operators').setup({ sort = { func = sort_func } })
------------------------------------------------------------------------------
*MiniOperators.evaluate()*
`MiniOperators.evaluate`({mode})
Evaluate text and replace with output
It replaces the region with the output of `config.evaluate.func`.
By default it is |MiniOperators.default_evaluate_func()| which evaluates
text as Lua code depending on the region submode.
Parameters ~
{mode} `(string|nil)` One of `nil`, `'char'`, `'line'`, `''block`, `'visual'`.
------------------------------------------------------------------------------
*MiniOperators.exchange()*
`MiniOperators.exchange`({mode})
Exchange text regions
Has two-step logic:
- First call remembers the region as the one to be exchanged and highlights it
with `MiniOperatorsExchangeFrom` highlight group.
- Second call performs the exchange. Basically, a two substeps action:
"yank both regions" and replace each one with another.
Notes:
- Use `<C-c>` to stop exchanging after the first step.
- Exchanged regions can have different (char,line,block)-wise submodes.
- Works with most cases of intersecting regions, but not officially supported.
Parameters ~
{mode} `(string|nil)` One of `nil`, `'char'`, `'line'`, `''block`, `'visual'`.
------------------------------------------------------------------------------
*MiniOperators.multiply()*
`MiniOperators.multiply`({mode})
Multiply (duplicate) text
Copies a region (without affecting registers) and puts it directly after.
Notes:
- Supports two types of |[count]|: `[count1]gm[count2][textobject]` with default
`config.multiply.prefix` makes `[count1]` copies of region defined by
`[count2][textobject]`. Example: `2gm3aw` - 2 copies of `3aw`.
- |[count]| for "line" mapping (`gmm` by default) is treated as `[count1]` from
previous note.
- Advantages of using this instead of "yank" + "paste":
- Doesn't modify any register, while separate steps need some register to
hold multiplied text.
- In most cases separate steps would be "yank" + "move cursor" + "paste",
while "multiply" makes it at once.
Parameters ~
{mode} `(string|nil)` One of `nil`, `'char'`, `'line'`, `''block`, `'visual'`.
------------------------------------------------------------------------------
*MiniOperators.replace()*
`MiniOperators.replace`({mode})
Replace text with register
Notes:
- Supports two types of |[count]|: `[count1]gr[count2][textobject]` with default
`config.replace.prefix` puts `[count1]` contents of register over region defined
by `[count2][textobject]`. Example: `2gr3aw` - 2 register contents over `3aw`.
- |[count]| for "line" mapping (`grr` by default) is treated as `[count1]` from
previous note.
- Advantages of using this instead of "visually select" + "paste with |v_P|":
- As operator it is dot-repeatable which has cumulative gain in case of
multiple replacing is needed.
- Can automatically reindent.
Parameters ~
{mode} `(string|nil)` One of `nil`, `'char'`, `'line'`, `''block`, `'visual'`.
------------------------------------------------------------------------------
*MiniOperators.sort()*
`MiniOperators.sort`({mode})
Sort text
It replaces the region with the output of `config.sort.func`.
By default it is |MiniOperators.default_sort_func()| which sorts the text
depending on submode.
Notes:
- "line" mapping is charwise (as there is not much sense in sorting
linewise a single line). This also results into no |[count]| support.
Parameters ~
{mode} `(string|nil)` One of `nil`, `'char'`, `'line'`, `''block`, `'visual'`.
------------------------------------------------------------------------------
*MiniOperators.make_mappings()*
`MiniOperators.make_mappings`({operator_name}, {lhs_tbl})
Make operator mappings
Parameters ~
{operator_name} `(string)` Name of existing operator from this module.
{lhs_tbl} `(table)` Table with mappings keys. Should have these fields:
- <textobject> `(string)` - Normal mode mapping to operate on textobject.
- <line> `(string)` - Normal mode mapping to operate on line.
Usually an alias for textobject mapping followed by |_|.
For "sort" it operates charwise on whole line without left and right
whitespace (as there is not much sense in sorting linewise a single line).
- <selection> `(string)` - Visual mode mapping to operate on selection.
Supply empty string to not create particular mapping. Note: creating `line`
mapping needs `textobject` mapping to be set.
Usage ~
>lua
require('mini.operators').make_mappings(
'replace',
{ textobject = 'cr', line = 'crr', selection = 'cr' }
)
<
------------------------------------------------------------------------------
*MiniOperators.default_evaluate_func()*
`MiniOperators.default_evaluate_func`({content})
Default evaluate function
Evaluate text as Lua code and return object from last line (like if last
line is prepended with `return` if it is not already).
Behavior depends on region submode:
- For charwise and linewise regions, text evaluated as is.
- For blockwise region, lines are evaluated per line using only first lines
of outputs. This allows separate execution of lines in order to provide
something different compared to linewise region.
Parameters ~
{content} `(table)` Table with the following fields:
- <lines> `(table)` - array with content lines.
- <submode> `(string)` - region submode. One of `'v'`, `'V'`, `'<C-v>'` (escaped).
------------------------------------------------------------------------------
*MiniOperators.default_sort_func()*
`MiniOperators.default_sort_func`({content}, {opts})
Default sort function
Sort text based on region submode:
- For charwise region, split by separator pattern, sort parts, merge back
with separators. Actual pattern is inferred based on the array of patterns
from `opts.split_patterns`: whichever element is present in the text is
used, preferring the earlier one if several are present.
Example: sorting "c, b; a" line with default `opts.split_patterns` results
into "b; a, c" as it is split only by comma.
- For linewise and blockwise regions sort lines as is.
Notes:
- Sort is done with |table.sort()| on an array of lines, which doesn't treat
whitespace or digits specially. Use |:sort| for more complicated tasks.
- Pattern is allowed to be an empty string in which case split results into
all characters as parts.
- Pad pattern in `split_patterns` with `%s*` to include whitespace into separator.
Example: line "b _ a" with "_" pattern will be sorted as " a_b " (because
it is split as "b ", "_", " a" ) while with "%s*_%s*" pattern it results
into "a _ b" (split as "b", " _ ", "a").
Parameters ~
{content} `(table)` Table with the following fields:
- <lines> `(table)` - array with content lines.
- <submode> `(string)` - region submode. One of `'v'`, `'V'`, `'<C-v>'` (escaped).
{opts} `(table|nil)` Options. Possible fields:
- <compare_fun> `(function)` - compare function compatible with |table.sort()|.
Default: direct compare with `<`.
- <split_patterns> `(table)` - array of split Lua patterns to be used for
charwise submode. Order is important.
Default: `{ '%s*,%s*', '%s*;%s*', '%s+', '' }`.
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -0,0 +1,342 @@
*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.
------------------------------------------------------------------------------
*MiniPairs.setup()*
`MiniPairs.setup`({config})
Module setup
Parameters ~
{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.config*
`MiniPairs.config`
Module config
Default values:
>lua
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 } },
},
}
<
------------------------------------------------------------------------------
*MiniPairs.map()*
`MiniPairs.map`({mode}, {lhs}, {pair_info}, {opts})
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`.
Parameters ~
{mode} `(string)` `mode` for |nvim_set_keymap()|.
{lhs} `(string)` `lhs` for |nvim_set_keymap()|.
{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).
{opts} `(table|nil)` Optional table `opts` for |nvim_set_keymap()|. Elements
`expr` and `noremap` won't be recognized (`true` by default).
------------------------------------------------------------------------------
*MiniPairs.map_buf()*
`MiniPairs.map_buf`({buffer}, {mode}, {lhs}, {pair_info}, {opts})
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`.
Parameters ~
{buffer} `(number)` `buffer` for |nvim_buf_set_keymap()|.
{mode} `(string)` `mode` for |nvim_buf_set_keymap()|.
{lhs} `(string)` `lhs` for |nvim_buf_set_keymap()|.
{pair_info} `(table)` Table with pair information.
{opts} `(table|nil)` Optional table `opts` for |nvim_buf_set_keymap()|.
Elements `expr` and `noremap` won't be recognized (`true` by default).
------------------------------------------------------------------------------
*MiniPairs.unmap()*
`MiniPairs.unmap`({mode}, {lhs}, {pair})
Remove global mapping
A wrapper for |nvim_del_keymap()| which registers supplied `pair`.
Parameters ~
{mode} `(string)` `mode` for |nvim_del_keymap()|.
{lhs} `(string)` `lhs` for |nvim_del_keymap()|.
{pair} `(string)` Pair which should be unregistered from both
`<BS>` and `<CR>`. Should be explicitly supplied to avoid confusion.
Supply `''` to not unregister pair.
------------------------------------------------------------------------------
*MiniPairs.unmap_buf()*
`MiniPairs.unmap_buf`({buffer}, {mode}, {lhs}, {pair})
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 })
<
Parameters ~
{buffer} `(number)` `buffer` for |nvim_buf_del_keymap()|.
{mode} `(string)` `mode` for |nvim_buf_del_keymap()|.
{lhs} `(string)` `lhs` for |nvim_buf_del_keymap()|.
{pair} `(string)` Pair which should be unregistered from both
`<BS>` and `<CR>`. Should be explicitly supplied to avoid confusion.
Supply `''` to not unregister pair.
------------------------------------------------------------------------------
*MiniPairs.open()*
`MiniPairs.open`({pair}, {neigh_pattern})
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.
Parameters ~
{pair} `(string)` String with two characters representing pair.
{neigh_pattern} `(string|nil)` Pattern for two neighborhood characters.
Character "\r" indicates line start, "\n" - line end.
Return ~
`(string)` Keys performing "open" action.
------------------------------------------------------------------------------
*MiniPairs.close()*
`MiniPairs.close`({pair}, {neigh_pattern})
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.
Parameters ~
{pair} `(string)` String with two characters representing pair.
{neigh_pattern} `(string|nil)` Pattern for two neighborhood characters.
Character "\r" indicates line start, "\n" - line end.
Return ~
`(string)` Keys performing "close" action.
------------------------------------------------------------------------------
*MiniPairs.closeopen()*
`MiniPairs.closeopen`({pair}, {neigh_pattern})
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.
Parameters ~
{pair} `(string)` String with two characters representing pair.
{neigh_pattern} `(string|nil)` Pattern for two neighborhood characters.
Character "\r" indicates line start, "\n" - line end.
Return ~
`(string)` Keys performing "closeopen" action.
------------------------------------------------------------------------------
*MiniPairs.bs()*
`MiniPairs.bs`({key})
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")')
<
Parameters ~
{key} `(string|nil)` Key to use. Default: `<BS>`.
Return ~
`(string)` Keys performing "backspace" action.
------------------------------------------------------------------------------
*MiniPairs.cr()*
`MiniPairs.cr`({key})
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|.
Parameters ~
{key} `(string|nil)` Key to use. Default: `<CR>`.
Return ~
`(string)` Keys performing "new line" action.
vim:tw=78:ts=8:noet:ft=help:norl:

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,226 @@
*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.
------------------------------------------------------------------------------
*MiniSessions.setup()*
`MiniSessions.setup`({config})
Module setup
Parameters ~
{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.config*
`MiniSessions.config`
Module config
Default values:
>lua
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)
directory = --<"session" subdir of user data directory from |stdpath()|>,
-- 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 },
}
<
------------------------------------------------------------------------------
*MiniSessions.detected*
`MiniSessions.detected`
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.read()*
`MiniSessions.read`({session_name}, {opts})
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.
Parameters ~
{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|).
{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.write()*
`MiniSessions.write`({session_name}, {opts})
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|.
Parameters ~
{session_name} `(string|nil)` Name of session file to write. Default: `nil` for
current session (|v:this_session|).
{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.delete()*
`MiniSessions.delete`({session_name}, {opts})
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|.
Parameters ~
{session_name} `(string|nil)` Name of detected session file to delete. Default:
`nil` for name of current session (taken from |v:this_session|).
{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.select()*
`MiniSessions.select`({action}, {opts})
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").
Parameters ~
{action} `(string|nil)` Action to perform. Should be one of "read" (default),
"write", or "delete".
{opts} `(table|nil)` Options for specified action.
------------------------------------------------------------------------------
*MiniSessions.get_latest()*
`MiniSessions.get_latest`()
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.
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -0,0 +1,537 @@
*mini.splitjoin* Split and join arguments
*MiniSplitjoin*
MIT License Copyright (c) 2023 Evgeni Chasnovski
==============================================================================
Features:
- Mappings and Lua functions that modify arguments (regions inside brackets
between allowed separators) under cursor.
Supported actions:
- Toggle - split if arguments are on single line, join otherwise.
Main supported function of the module. See |MiniSplitjoin.toggle()|.
- Split - make every argument separator be on end of separate line.
See |MiniSplitjoin.split()|.
- Join - make all arguments be on single line.
See |MiniSplitjoin.join()|.
- Mappings are dot-repeatable in Normal mode and work in Visual mode.
- Customizable argument detection (see |MiniSplitjoin.config.detect|):
- Which brackets can contain arguments.
- Which strings can separate arguments.
- Which regions are excluded when looking for separators (like inside
nested brackets or quotes).
- Customizable pre and post hooks for both split and join. See `split` and
`join` in |MiniSplitjoin.config|. There are several built-in ones
in |MiniSplitjoin.gen_hook|.
- Works inside comments by using modified notion of indent.
See |MiniSplitjoin.get_indent_part()|.
- Provides low-level Lua functions for split and join at positions.
See |MiniSplitjoin.split_at()| and |MiniSplitjoin.join_at()|.
Notes:
- Search for arguments is done using Lua patterns (regex-like approach).
Certain amount of false positives is to be expected.
- This module is mostly designed around |MiniSplitjoin.toggle()|. If target
split positions are on different lines, join first and then split.
- Actions can be done on Visual mode selection, which mostly present as
a safety route in case of incorrect detection of initial region.
It uses |MiniSplitjoin.get_visual_region()| which treats selection as full
brackets (include brackets in selection).
# Setup ~
This module needs a setup with `require('mini.splitjoin').setup({})` (replace
`{}` with your `config` table). It will create global Lua table `MiniSplitjoin`
which you can use for scripting or manually (with `:lua MiniSplitjoin.*`).
See |MiniSplitjoin.config| for available config settings.
You can override runtime config settings (like action hooks) locally to
buffer inside `vim.b.minisplitjoin_config` which should have same structure
as `MiniSplitjoin.config`. See |mini.nvim-buffer-local-config| for more details.
# Comparisons ~
- 'FooSoft/vim-argwrap':
- Mostly has the same design as this module.
- Doesn't work inside comments, while this module does.
- Has more built-in ways to control split and join, while this module
intentionally provides only handful.
- 'AndrewRadev/splitjoin.vim':
- More oriented towards language-depended transformations, while this
module intntionally deals with more generic text-related functionality.
- 'Wansmer/treesj':
- Operates based on tree-sitter nodes. This is more accurate in
some edge cases, but **requires** tree-sitter parser.
- Doesn't work inside comments or strings.
# Disabling ~
To disable, set `g:minisplitjoin_disable` (globally) or `b:minisplitjoin_disable`
(for a buffer) to `v: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.
------------------------------------------------------------------------------
*MiniSplitjoin-glossary*
- POSITION - table with fields <line> and <col> containing line and column
numbers respectively. Both are 1-indexed. Example: `{ line = 2, col = 1 }`.
- REGION - table representing region in a buffer. Fields: <from> and <to> for
inclusive start and end positions. Example: >lua
{ from = { line = 1, col = 1 }, to = { line = 2, col = 1 } }
<
------------------------------------------------------------------------------
*MiniSplitjoin.setup()*
`MiniSplitjoin.setup`({config})
Module setup
Parameters ~
{config} `(table|nil)` Module config table. See |MiniSplitjoin.config|.
Usage ~
>lua
require('mini.splitjoin').setup() -- use default config
-- OR
require('mini.splitjoin').setup({}) -- replace {} with your config table
<
------------------------------------------------------------------------------
*MiniSplitjoin.config*
`MiniSplitjoin.config`
Module config
Default values:
>lua
MiniSplitjoin.config = {
-- Module mappings. Use `''` (empty string) to disable one.
-- Created for both Normal and Visual modes.
mappings = {
toggle = 'gS',
split = '',
join = '',
},
-- Detection options: where split/join should be done
detect = {
-- Array of Lua patterns to detect region with arguments.
-- Default: { '%b()', '%b[]', '%b{}' }
brackets = nil,
-- String Lua pattern defining argument separator
separator = ',',
-- Array of Lua patterns for sub-regions to exclude separators from.
-- Enables correct detection in presence of nested brackets and quotes.
-- Default: { '%b()', '%b[]', '%b{}', '%b""', "%b''" }
exclude_regions = nil,
},
-- Split options
split = {
hooks_pre = {},
hooks_post = {},
},
-- Join options
join = {
hooks_pre = {},
hooks_post = {},
},
}
<
*MiniSplitjoin.config.detect*
# Detection ~
The table at `config.detect` controls how arguments are detected using Lua
patterns. General idea is to convert whole buffer into a single line,
perform string search, and convert results back into 2d positions.
Example configuration: >lua
require('mini.splitjoin').setup({
detect = {
-- Detect only inside balanced parenthesis
brackets = { '%b()' },
-- Allow both `,` and `;` to separate arguments
separator = '[,;]',
-- Make any separator define an argument
exclude_regions = {},
},
})
<
## Outer brackets ~
`detect.brackets` is an array of Lua patterns used to find enclosing region.
It is done by traversing whole buffer to find the smallest region matching
any supplied pattern.
Default: `nil`, inferred as `{ '%b()', '%b[]', '%b{}' }`.
So an argument can be inside a balanced `()`, `[]`, or `{}`.
Example: `brackets = { '%b()' }` will search for arguments only inside
balanced `()`.
## Separator ~
`detect.separator` is a single Lua pattern defining which strings should be
treated as argument separators.
Empty string in `detect.separator` will result in only surrounding brackets
used as separators.
Only end of pattern match will be used as split/join positions.
Default: `','`. So an argument can be separated only with comma.
Example: `separator = { '[,;]' }` will treat both `,` and `;` as separators.
## Excluded regions ~
`detect.exclude_regions` is an array of Lua patterns for sub-regions from which
to exclude separators. Enables correct detection in case of nested brackets
and quotes.
Default: `nil`; inferred as `{ '%b()', '%b[]', '%b{}', '%b""', "%b''" }`.
So a separator **can not** be inside a balanced `()`, `[]`, `{}` (representing
nested argument regions) or `""`, `''` (representing strings).
Example: `exclude_regions = {}` will not exclude any regions. So in case of
`f(a, { b, c })` it will detect both commas as argument separators.
# Hooks ~
`split.hooks_pre`, `split.hooks_post`, `join.hooks_pre`, and `join.hooks_post`
are arrays of hook functions. If empty (default) no hook is applied.
Hooks should take and return array of positions. See |MiniSplitjoin-glossary|.
They can be used to tweak actions:
- Pre-hooks are called before action. Each is applied on the output of
previous one. Input of first hook are detected split/join positions.
Output of last one is actually used to perform split/join.
- Post-hooks are called after action. Each is applied on the output of
previous one. Input of first hook are split/join positions from actual
action plus its region's right end as last position (for easier hook code).
Output of last one is used as action return value.
For more specific details see |MiniSplitjoin.split()| and |MiniSplitjoin.join()|.
See |MiniSplitjoin.gen_hook| for generating common hooks with examples.
------------------------------------------------------------------------------
*MiniSplitjoin.toggle()*
`MiniSplitjoin.toggle`({opts})
Toggle arguments
Overview:
- Detect region at input position: either by using supplied `opts.region` or
by finding smallest bracketed region surrounding position.
See |MiniSplitjoin.config.detect| for more details.
- If region spans single line, use |MiniSplitjoin.split()| with found region.
Otherwise use |MiniSplitjoin.join()|.
Parameters ~
{opts} `(table|nil)` Options. Has structure from |MiniSplitjoin.config|
inheriting its default values.
Following extra optional fields are allowed:
- <position> `(table)` - position at which to find smallest bracket region.
See |MiniSplitjoin-glossary| for the structure.
Default: cursor position.
- <region> `(table)` - region at which to perform action. Assumes inclusive
both start at left bracket and end at right bracket.
See |MiniSplitjoin-glossary| for the structure.
Default: `nil` to automatically detect region.
Return ~
`(any)` Output of chosen `split()` or `join()` action.
------------------------------------------------------------------------------
*MiniSplitjoin.split()*
`MiniSplitjoin.split`({opts})
Split arguments
Overview:
- Detect region: either by using supplied `opts.region` or by finding smallest
bracketed region surrounding input position (cursor position by default).
See |MiniSplitjoin.config.detect| for more details.
- Find separator positions using `separator` and `exclude_regions` from `opts`.
Both brackets are treated as separators.
See |MiniSplitjoin.config.detect| for more details.
Note: stop if no separator positions are found.
- Modify separator positions to represent split positions. Last split position
(which is inferred from right bracket) is moved one column to left so that
right bracket would move on new line.
- Apply all hooks from `opts.split.hooks_pre`. Each is applied on the output of
previous one. Input of first hook is split positions from previous step.
Output of last one is used as split positions in next step.
- Split and update split positions with |MiniSplitjoin.split_at()|.
- Apply all hooks from `opts.split.hooks_post`. Each is applied on the output of
previous one. Input of first hook is split positions from previous step plus
region's right end (for easier hook code).
Output of last one is used as function return value.
Note:
- By design, it doesn't detect if argument **should** be split, so application
on arguments spanning multiple lines can lead to undesirable result.
Parameters ~
{opts} `(table|nil)` Options. Has structure from |MiniSplitjoin.config|
inheriting its default values.
Following extra optional fields are allowed:
- <position> `(table)` - position at which to find smallest bracket region.
See |MiniSplitjoin-glossary| for the structure.
Default: cursor position.
- <region> `(table)` - region at which to perform action. Assumes inclusive
both start at left bracket and end at right bracket.
See |MiniSplitjoin-glossary| for the structure.
Default: `nil` to automatically detect region.
Return ~
`(any)` Output of last `opts.split.hooks_post` or `nil` if no split positions
found. Default: return value of |MiniSplitjoin.split_at()| application.
------------------------------------------------------------------------------
*MiniSplitjoin.join()*
`MiniSplitjoin.join`({opts})
Join arguments
Overview:
- Detect region: either by using supplied `opts.region` or by finding smallest
bracketed region surrounding input position (cursor position by default).
See |MiniSplitjoin.config.detect| for more details.
- Compute join positions to be line ends of all but last region lines.
Note: stop if no join positions are found.
- Apply all hooks from `opts.join.hooks_pre`. Each is applied on the output
of previous one. Input of first hook is join positions from previous step.
Output of last one is used as join positions in next step.
- Join and update join positions with |MiniSplitjoin.join_at()|.
- Apply all hooks from `opts.join.hooks_post`. Each is applied on the output
of previous one. Input of first hook is join positions from previous step
plus region's right end for easier hook code.
Output of last one is used as function return value.
Parameters ~
{opts} `(table|nil)` Options. Has structure from |MiniSplitjoin.config|
inheriting its default values.
Following extra optional fields are allowed:
- <position> `(table)` - position at which to find smallest bracket region.
See |MiniSplitjoin-glossary| for the structure.
Default: cursor position.
- <region> `(table)` - region at which to perform action. Assumes inclusive
both start at left bracket and end at right bracket.
See |MiniSplitjoin-glossary| for the structure.
Default: `nil` to automatically detect region.
Return ~
`(any)` Output of last `opts.split.hooks_post` or `nil` of no join positions
found. Default: return value of |MiniSplitjoin.join_at()| application.
------------------------------------------------------------------------------
*MiniSplitjoin.gen_hook*
`MiniSplitjoin.gen_hook`
Generate common hooks
This is a table with function elements. Call to actually get hook.
All generated post-hooks return updated versions of their input reflecting
changes done inside hook.
Example for `lua` filetype (place it in 'lua.lua' filetype plugin, |ftplugin|): >lua
local gen_hook = MiniSplitjoin.gen_hook
local curly = { brackets = { '%b{}' } }
-- Add trailing comma when splitting inside curly brackets
local add_comma_curly = gen_hook.add_trailing_separator(curly)
-- Delete trailing comma when joining inside curly brackets
local del_comma_curly = gen_hook.del_trailing_separator(curly)
-- Pad curly brackets with single space after join
local pad_curly = gen_hook.pad_brackets(curly)
-- Create buffer-local config
vim.b.minisplitjoin_config = {
split = { hooks_post = { add_comma_curly } },
join = { hooks_post = { del_comma_curly, pad_curly } },
}
<
------------------------------------------------------------------------------
*MiniSplitjoin.gen_hook.pad_brackets()*
`MiniSplitjoin.gen_hook.pad_brackets`({opts})
Generate hook to pad brackets
This is a join post-hook. Use in `join.hooks_post` of |MiniSplitjoin.config|.
Parameters ~
{opts} `(table|nil)` Options. Possible fields:
- <pad> `(string)` - pad to add after first and before last join positions.
Default: `' '` (single space).
- <brackets> `(table)` - array of bracket patterns indicating on which
brackets action should be made. Has same structure as `brackets`
in |MiniSplitjoin.config.detect|.
Default: `MiniSplitjoin.config.detect.brackets`.
Return ~
`(function)` A hook which adds inner pad to first and last join positions and
returns updated input join positions.
------------------------------------------------------------------------------
*MiniSplitjoin.gen_hook.add_trailing_separator()*
`MiniSplitjoin.gen_hook.add_trailing_separator`({opts})
Generate hook to add trailing separator
This is a split post-hook. Use in `split.hooks_post` of |MiniSplitjoin.config|.
Parameters ~
{opts} `(table|nil)` Options. Possible fields:
- <sep> `(string)` - separator to add before last split position.
Default: `','`.
- <brackets> `(table)` - array of bracket patterns indicating on which
brackets action should be made. Has same structure as `brackets`
in |MiniSplitjoin.config.detect|.
Default: `MiniSplitjoin.config.detect.brackets`.
Return ~
`(function)` A hook which adds separator before last split position and
returns updated input split positions.
------------------------------------------------------------------------------
*MiniSplitjoin.gen_hook.del_trailing_separator()*
`MiniSplitjoin.gen_hook.del_trailing_separator`({opts})
Generate hook to delete trailing separator
This is a join post-hook. Use in `join.hooks_post` of |MiniSplitjoin.config|.
Parameters ~
{opts} `(table|nil)` Options. Possible fields:
- <sep> `(string)` - separator to remove before last join position.
Default: `','`.
- <brackets> `(table)` - array of bracket patterns indicating on which
brackets action should be made. Has same structure as `brackets`
in |MiniSplitjoin.config.detect|.
Default: `MiniSplitjoin.config.detect.brackets`.
Return ~
`(function)` A hook which adds separator before last split position and
returns updated input split positions.
------------------------------------------------------------------------------
*MiniSplitjoin.split_at()*
`MiniSplitjoin.split_at`({positions})
Split at positions
Overview:
- For each position move all characters after it to next line and make it have
same indent as current one (see |MiniSplitjoin.get_indent_part()|).
Also remove trailing whitespace at position line.
- Increase indent of inner lines by a single pad: tab in case of |noexpandtab|
or |shiftwidth()| number of spaces otherwise.
Notes:
- Cursor is adjusted to follow text updates.
- Use output of this function to keep track of input positions.
Parameters ~
{positions} `(table)` Array of positions at which to perform split.
See |MiniSplitjoin-glossary| for their structure. Note: they don't have
to be ordered, but first and last ones will be used to infer lines for
which indent will be increased.
Return ~
`(table)` Array of new positions to where input `positions` were moved.
------------------------------------------------------------------------------
*MiniSplitjoin.join_at()*
`MiniSplitjoin.join_at`({positions})
Join at positions
Overview:
- For each position join its line with the next line. Joining is done by
replacing trailing whitespace of the line and indent of its next line
(see |MiniSplitjoin.get_indent_part()|) with a pad string (single space except
empty string for first and last positions). To adjust this, use hooks
(for example, see |MiniSplitjoin.gen_hook.pad_brackets()|).
Notes:
- Cursor is adjusted to follow text updates.
- Use output of this function to keep track of input positions.
Parameters ~
{positions} `(table)` Array of positions at which to perform join.
See |MiniSplitjoin-glossary| for their structure. Note: they don't have
to be ordered, but first and last ones will have different pad string.
Return ~
`(table)` Array of new positions to where input `positions` were moved.
------------------------------------------------------------------------------
*MiniSplitjoin.get_visual_region()*
`MiniSplitjoin.get_visual_region`()
Get previous visual region
Get previous visual selection using |`<| and |`>| marks in the format of
region (see |MiniSplitjoin-glossary|). Used in Visual mode mappings.
Note:
- Both marks are included in region, so for better
- In linewise Visual mode
Return ~
`(table)` A region. See |MiniSplitjoin-glossary| for exact structure.
------------------------------------------------------------------------------
*MiniSplitjoin.get_indent_part()*
`MiniSplitjoin.get_indent_part`({line}, {respect_comments})
Get string's indent part
Parameters ~
{line} `(string)` String for which to compute indent.
{respect_comments} `(boolean|nil)` Whether to respect comments as indent part.
Default: `true`.
Return ~
`(string)` Part of input representing line's indent. Can be empty string.
Use `string.len()` to compute indent in bytes.
------------------------------------------------------------------------------
*MiniSplitjoin.operator()*
`MiniSplitjoin.operator`({task})
Operator for Normal mode mappings
Main function to be used in expression mappings. No need to use it
directly, everything is setup in |MiniSplitjoin.setup()|.
Parameters ~
{task} `(string)` Name of task.
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -0,0 +1,623 @@
*mini.starter* Start screen
*MiniStarter*
MIT License Copyright (c) 2021 Evgeni Chasnovski
==============================================================================
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.
Key design ideas:
- All available actions are defined inside items. Each item should have the
following info:
- <action> - function or string for |vim.cmd| which is executed when
item is chosen. Empty string result in placeholder "inactive" item.
- <name> - string which will be displayed and used for choosing.
- <section> - string representing to which section item belongs.
There are pre-configured whole sections in |MiniStarter.sections|.
- Configure what items are displayed by supplying an array which can be
normalized to an array of items. Read about how supplied items are
normalized in |MiniStarter.refresh|.
- Modify the final look by supplying content hooks: functions which take
buffer content (see |MiniStarter.get_content()|) and identifier as input
while returning buffer content as output. There are pre-configured
content hook generators in |MiniStarter.gen_hook|.
- Choosing an item can be done in two ways:
- Type prefix query to filter item by matching its name (ignoring
case). Displayed information is updated after every typed character.
For every item its unique prefix is highlighted.
- Use Up/Down arrows and hit Enter.
- Allow multiple simultaneously open Starter buffers.
What is doesn't do:
- It doesn't support fuzzy query for items. And probably will never do.
# Setup ~
This module needs a setup with `require('mini.starter').setup({})`
(replace `{}` with your `config` table). It will create global Lua table
`MiniStarter` which you can use for scripting or manually (with
`:lua MiniStarter.*`).
See |MiniStarter.config| for `config` structure and default values. For
some configuration examples (including one similar to 'vim-startify' and
'dashboard-nvim'), see |MiniStarter-example-config|.
You can override runtime config settings locally to buffer inside
`vim.b.ministarter_config` which should have same structure as
`MiniStarter.config`. See |mini.nvim-buffer-local-config| for more details.
Note: `vim.b.ministarter_config` is copied to Starter buffer from current
buffer allowing full customization.
To stop module from showing non-error feedback, set `config.silent = true`.
# Highlight groups ~
* `MiniStarterCurrent` - current item.
* `MiniStarterFooter` - footer units.
* `MiniStarterHeader` - header units.
* `MiniStarterInactive` - inactive item.
* `MiniStarterItem` - item name.
* `MiniStarterItemBullet` - units from |MiniStarter.gen_hook.adding_bullet|.
* `MiniStarterItemPrefix` - unique query for item.
* `MiniStarterSection` - section units.
* `MiniStarterQuery` - current query in active items.
To change any highlight group, modify it directly with |:highlight|.
# Disabling ~
To disable core functionality, set `vim.g.ministarter_disable` (globally) or
`vim.b.ministarter_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.
------------------------------------------------------------------------------
*MiniStarter-example-config*
Example configurations
Configuration similar to 'mhinz/vim-startify': >lua
local starter = require('mini.starter')
starter.setup({
evaluate_single = true,
items = {
starter.sections.builtin_actions(),
starter.sections.recent_files(10, false),
starter.sections.recent_files(10, true),
-- Use this if you set up 'mini.sessions'
starter.sections.sessions(5, true)
},
content_hooks = {
starter.gen_hook.adding_bullet(),
starter.gen_hook.indexing('all', { 'Builtin actions' }),
starter.gen_hook.padding(3, 2),
},
})
<
Configuration similar to 'glepnir/dashboard-nvim': >lua
local starter = require('mini.starter')
starter.setup({
items = {
starter.sections.telescope(),
},
content_hooks = {
starter.gen_hook.adding_bullet(),
starter.gen_hook.aligning('center', 'center'),
},
})
<
Elaborated configuration showing capabilities of custom items,
header/footer, and content hooks: >lua
local my_items = {
{ name = 'Echo random number', action = 'lua print(math.random())', section = 'Section 1' },
function()
return {
{ name = 'Item #1 from function', action = [[echo 'Item #1']], section = 'From function' },
{ name = 'Placeholder (always inactive) item', action = '', section = 'From function' },
function()
return {
name = 'Item #1 from double function',
action = [[echo 'Double function']],
section = 'From double function',
}
end,
}
end,
{ name = [[Another item in 'Section 1']], action = 'lua print(math.random() + 10)', section = 'Section 1' },
}
local footer_n_seconds = (function()
local timer = vim.loop.new_timer()
local n_seconds = 0
timer:start(0, 1000, vim.schedule_wrap(function()
if vim.bo.filetype ~= 'ministarter' then
timer:stop()
return
end
n_seconds = n_seconds + 1
MiniStarter.refresh()
end))
return function()
return 'Number of seconds since opening: ' .. n_seconds
end
end)()
local hook_top_pad_10 = function(content)
-- Pad from top
for _ = 1, 10 do
-- Insert at start a line with single content unit
table.insert(content, 1, { { type = 'empty', string = '' } })
end
return content
end
local starter = require('mini.starter')
starter.setup({
items = my_items,
footer = footer_n_seconds,
content_hooks = { hook_top_pad_10 },
})
<
------------------------------------------------------------------------------
*MiniStarter-lifecycle*
# Lifecycle of Starter buffer ~
- Open with |MiniStarter.open()|. It includes creating buffer with
appropriate options, mappings, behavior; call to |MiniStarter.refresh()|;
issue `MiniStarterOpened` |User| event.
- Wait for user to choose an item. This is done using following logic:
- Typing any character from `MiniStarter.config.query_updaters` leads
to updating query. Read more in |MiniStarter.add_to_query|.
- <BS> deletes latest character from query.
- <Down>/<Up>, <C-n>/<C-p>, <M-j>/<M-k> move current item.
- <CR> executes action of current item.
- <C-c> closes Starter buffer.
- Evaluate current item when appropriate (after `<CR>` or when there is a
single item and `MiniStarter.config.evaluate_single` is `true`). This
executes item's `action`.
------------------------------------------------------------------------------
*MiniStarter.setup()*
`MiniStarter.setup`({config})
Module setup
Parameters ~
{config} `(table|nil)` Module config table. See |MiniStarter.config|.
Usage ~
>lua
require('mini.starter').setup() -- use default config
-- OR
require('mini.starter').setup({}) -- replace {} with your config table
<
------------------------------------------------------------------------------
*MiniStarter.config*
`MiniStarter.config`
Module config
Default values:
>lua
MiniStarter.config = {
-- Whether to open Starter buffer on VimEnter. Not opened if Neovim was
-- started with intent to show something else.
autoopen = true,
-- Whether to evaluate action of single active item
evaluate_single = false,
-- Items to be displayed. Should be an array with the following elements:
-- - Item: table with <action>, <name>, and <section> keys.
-- - Function: should return one of these three categories.
-- - Array: elements of these three types (i.e. item, array, function).
-- If `nil` (default), default items will be used (see |mini.starter|).
items = nil,
-- Header to be displayed before items. Converted to single string via
-- `tostring` (use `\n` to display several lines). If function, it is
-- evaluated first. If `nil` (default), polite greeting will be used.
header = nil,
-- Footer to be displayed after items. Converted to single string via
-- `tostring` (use `\n` to display several lines). If function, it is
-- evaluated first. If `nil` (default), default usage help will be shown.
footer = nil,
-- Array of functions to be applied consecutively to initial content.
-- Each function should take and return content for Starter buffer (see
-- |mini.starter| and |MiniStarter.get_content()| for more details).
content_hooks = nil,
-- Characters to update query. Each character will have special buffer
-- mapping overriding your global ones. Be careful to not add `:` as it
-- allows you to go into command mode.
query_updaters = 'abcdefghijklmnopqrstuvwxyz0123456789_-.',
-- Whether to disable showing non-error feedback
silent = false,
}
<
------------------------------------------------------------------------------
*MiniStarter.open()*
`MiniStarter.open`({buf_id})
Open Starter buffer
- Create buffer if necessary and move into it.
- Set buffer options. Note that settings are done with |noautocmd| to
achieve a massive speedup.
- Set buffer mappings. Besides basic mappings (described inside "Lifecycle
of Starter buffer" of |mini.starter|), map every character from
`MiniStarter.config.query_updaters` to add itself to query with
|MiniStarter.add_to_query|.
- Populate buffer with |MiniStarter.refresh|.
- Issue custom `MiniStarterOpened` event to allow acting upon opening
Starter buffer. Use it with
`autocmd User MiniStarterOpened <your command>`.
Note: to fully use it in autocommand, use |autocmd-nested|. Example: >lua
local starter_open = function() MiniStarter.open() end
local au_opts = { nested = true, callback = starter_open }
vim.api.nvim_create_autocmd('TabNewEntered', au_opts)
<
Parameters ~
{buf_id} `(number|nil)` Identifier of existing valid buffer (see |bufnr()|) to
open inside. Default: create a new one.
------------------------------------------------------------------------------
*MiniStarter.refresh()*
`MiniStarter.refresh`({buf_id})
Refresh Starter buffer
- Normalize `MiniStarter.config.items`:
- Flatten: recursively (in depth-first fashion) parse its elements. If
function is found, execute it and continue with parsing its output
(this allows deferring item collection up until it is actually
needed). If proper item is found (table with fields `action`,
`name`, `section`), add it to output.
- Sort: order first by section and then by item id (both in order of
appearance).
- Normalize `MiniStarter.config.header` and `MiniStarter.config.footer` to
be multiple lines by splitting at `\n`. If function - evaluate it first.
- Make initial buffer content (see |MiniStarter.get_content()| for a
description of what a buffer content is). It consist from content lines
with single content unit:
- First lines contain strings of normalized header.
- Body is for normalized items. Section names have own lines preceded
by empty line.
- Last lines contain separate strings of normalized footer.
- Sequentially apply hooks from `MiniStarter.config.content_hooks` to
content. All hooks are applied with `(content, buf_id)` signature. Output
of one hook serves as first argument to the next.
- Gather final items from content with |MiniStarter.content_to_items|.
- Convert content to buffer lines with |MiniStarter.content_to_lines| and
add them to buffer.
- Add highlighting of content units.
- Position cursor.
- Make current query. This results into some items being marked as
"inactive" and updating highlighting of current query on "active" items.
Note: this function is executed on every |VimResized| to allow more
responsive behavior.
Parameters ~
{buf_id} `(number|nil)` Buffer identifier of a valid Starter buffer.
Default: current buffer.
------------------------------------------------------------------------------
*MiniStarter.close()*
`MiniStarter.close`({buf_id})
Close Starter buffer
Parameters ~
{buf_id} `(number|nil)` Buffer identifier of a valid Starter buffer.
Default: current buffer.
------------------------------------------------------------------------------
*MiniStarter.sections*
`MiniStarter.sections`
Table of pre-configured sections
------------------------------------------------------------------------------
*MiniStarter.sections.builtin_actions()*
`MiniStarter.sections.builtin_actions`()
Section with builtin actions
Return ~
`(table)` Array of items.
------------------------------------------------------------------------------
*MiniStarter.sections.sessions()*
`MiniStarter.sections.sessions`({n}, {recent})
Section with |MiniSessions| sessions
Sessions are taken from |MiniSessions.detected|. Notes:
- If it shows "'mini.sessions' is not set up", it means that you didn't
call `require('mini.sessions').setup()`.
- If it shows "There are no detected sessions in 'mini.sessions'", it means
that there are no sessions at the current sessions directory. Either
create session or supply different directory where session files are
stored (see |MiniSessions.setup|).
- Local session (if detected) is always displayed first.
Parameters ~
{n} `(number|nil)` Number of returned items. Default: 5.
{recent} `(boolean|nil)` Whether to use recent sessions (instead of
alphabetically by name). Default: true.
Return ~
`(function)` Function which returns array of items.
------------------------------------------------------------------------------
*MiniStarter.sections.recent_files()*
`MiniStarter.sections.recent_files`({n}, {current_dir}, {show_path})
Section with most recently used files
Files are taken from |vim.v.oldfiles|.
Parameters ~
{n} `(number|nil)` Number of returned items. Default: 5.
{current_dir} `(boolean|nil)` Whether to return files only from current working
directory and its subdirectories. Default: `false`.
{show_path} `(boolean|function|nil)` Whether to append file name with its path.
If callable, will be called with full path and should return string to be
directly appended to file name. Default: `true`.
Return ~
`(function)` Function which returns array of items.
------------------------------------------------------------------------------
*MiniStarter.sections.pick()*
`MiniStarter.sections.pick`()
Section with 'mini.pick' pickers
Notes:
- All actions require |mini.pick| module of 'mini.nvim'.
- "Command history", "Explorer", and "Visited paths" items
require |mini.extra| module of 'mini.nvim'.
- "Visited paths" items requires |mini.visits| module of 'mini.nvim'.
Return ~
`(function)` Function which returns array of items.
------------------------------------------------------------------------------
*MiniStarter.sections.telescope()*
`MiniStarter.sections.telescope`()
Section with basic Telescope pickers relevant to start screen
Notes:
- All actions require 'nvim-telescope/telescope.nvim' plugin.
- "Browser" item requires 'nvim-telescope/telescope-file-browser.nvim'.
Return ~
`(function)` Function which returns array of items.
------------------------------------------------------------------------------
*MiniStarter.gen_hook*
`MiniStarter.gen_hook`
Table with pre-configured content hook generators
Each element is a function which returns content hook. So to use them
inside |MiniStarter.setup|, call them.
------------------------------------------------------------------------------
*MiniStarter.gen_hook.padding()*
`MiniStarter.gen_hook.padding`({left}, {top})
Hook generator for padding
Output is a content hook which adds constant padding from left and top.
This allows tweaking the screen position of buffer content.
Parameters ~
{left} `(number|nil)` Number of empty spaces to add to start of each content
line. Default: 0.
{top} `(number|nil)` Number of empty lines to add to start of content.
Default: 0.
Return ~
`(function)` Content hook.
------------------------------------------------------------------------------
*MiniStarter.gen_hook.adding_bullet()*
`MiniStarter.gen_hook.adding_bullet`({bullet}, {place_cursor})
Hook generator for adding bullet to items
Output is a content hook which adds supplied string to be displayed to the
left of item.
Parameters ~
{bullet} `(string|nil)` String to be placed to the left of item name.
Default: "░ ".
{place_cursor} `(boolean|nil)` Whether to place cursor on the first character
of bullet when corresponding item becomes current. Default: true.
Return ~
`(function)` Content hook.
------------------------------------------------------------------------------
*MiniStarter.gen_hook.indexing()*
`MiniStarter.gen_hook.indexing`({grouping}, {exclude_sections})
Hook generator for indexing items
Output is a content hook which adds unique index to the start of item's
name. It results into shortening queries required to choose an item (at
expense of clarity).
Parameters ~
{grouping} `(string|nil)` One of "all" (number indexing across all sections) or
"section" (letter-number indexing within each section). Default: "all".
{exclude_sections} `(table|nil)` Array of section names (values of `section`
element of item) for which index won't be added. Default: `{}`.
Return ~
`(function)` Content hook.
------------------------------------------------------------------------------
*MiniStarter.gen_hook.aligning()*
`MiniStarter.gen_hook.aligning`({horizontal}, {vertical})
Hook generator for aligning content
Output is a content hook which independently aligns content horizontally
and vertically. Window width and height are taken from first window in current
tabpage displaying the Starter buffer.
Basically, this computes left and top pads for |MiniStarter.gen_hook.padding|
such that output lines would appear aligned in certain way.
Parameters ~
{horizontal} `(string|nil)` One of "left", "center", "right". Default: "left".
{vertical} `(string|nil)` One of "top", "center", "bottom". Default: "top".
Return ~
`(function)` Content hook.
------------------------------------------------------------------------------
*MiniStarter.get_content()*
`MiniStarter.get_content`({buf_id})
Get content of Starter buffer
Generally, buffer content is a table in the form of "2d array" (or rather
"2d list" because number of elements can differ):
- Each element represents content line: an array with content units to be
displayed in one buffer line.
- Each content unit is a table with at least the following elements:
- "type" - string with type of content. Something like "item",
"section", "header", "footer", "empty", etc.
- "string" - which string should be displayed. May be an empty string.
- "hl" - which highlighting should be applied to content string. May be
`nil` for no highlighting.
See |MiniStarter.content_to_lines| for converting content to buffer lines
and |MiniStarter.content_to_items| - to list of parsed items.
Notes:
- Content units with type "item" also have `item` element with all
information about an item it represents. Those elements are used directly
to create an array of items used for query.
Parameters ~
{buf_id} `(number|nil)` Buffer identifier of a valid Starter buffer.
Default: current buffer.
------------------------------------------------------------------------------
*MiniStarter.content_coords()*
`MiniStarter.content_coords`({content}, {predicate})
Helper to iterate through content
Basically, this traverses content "2d array" (in depth-first fashion; top
to bottom, left to right) and returns "coordinates" of units for which
`predicate` is true-ish.
Parameters ~
{content} `(table|nil)` Content "2d array". Default: content of current buffer.
{predicate} `(function|string|nil)` Predictate to filter units. If it is:
- Function, then it is evaluated with unit as input.
- String, then it checks unit to have this type (allows easy getting of
units with some type).
- `nil`, all units are kept.
Return ~
`(table)` Array of resulting units' coordinates. Each coordinate is a
table with <line> and <unit> keys. To retrieve actual unit from coordinate
`c`, use `content[c.line][c.unit]`.
------------------------------------------------------------------------------
*MiniStarter.content_to_lines()*
`MiniStarter.content_to_lines`({content})
Convert content to buffer lines
One buffer line is made by concatenating `string` element of units within
same content line.
Parameters ~
{content} `(table|nil)` Content "2d array". Default: content of current buffer.
Return ~
`(table)` Array of strings for each buffer line.
------------------------------------------------------------------------------
*MiniStarter.content_to_items()*
`MiniStarter.content_to_items`({content})
Convert content to items
Parse content (in depth-first fashion) and retrieve each item from `item`
element of content units with type "item". This also:
- Computes some helper information about how item will be actually
displayed (after |MiniStarter.content_to_lines|) and minimum number of
prefix characters needed for a particular item to be queried single.
- Modifies item's `name` element taking it from corresponding `string`
element of content unit. This allows modifying item's `name` at the stage
of content hooks (like, for example, in |MiniStarter.gen_hook.indexing|).
Parameters ~
{content} `(table|nil)` Content "2d array". Default: content of current buffer.
Return ~
`(table)` Array of items.
------------------------------------------------------------------------------
*MiniStarter.eval_current_item()*
`MiniStarter.eval_current_item`({buf_id})
Evaluate current item
Note that it resets current query before evaluation, as it is rarely needed
any more.
Parameters ~
{buf_id} `(number|nil)` Buffer identifier of a valid Starter buffer.
Default: current buffer.
------------------------------------------------------------------------------
*MiniStarter.update_current_item()*
`MiniStarter.update_current_item`({direction}, {buf_id})
Update current item
This makes next (with respect to `direction`) active item to be current.
Parameters ~
{direction} `(string)` One of "next" or "previous".
{buf_id} `(number|nil)` Buffer identifier of a valid Starter buffer.
Default: current buffer.
------------------------------------------------------------------------------
*MiniStarter.add_to_query()*
`MiniStarter.add_to_query`({char}, {buf_id})
Add character to current query
- Update current query by appending `char` to its end (only if it results
into at least one active item) or delete latest character if `char` is `nil`.
- Recompute status of items: "active" if its name starts with new query,
"inactive" otherwise.
- Update highlighting: whole strings for "inactive" items, current query
for "active" items.
Parameters ~
{char} `(string|nil)` Single character to be added to query. If `nil`, deletes
latest character from query.
{buf_id} `(number|nil)` Buffer identifier of a valid Starter buffer.
Default: current buffer.
------------------------------------------------------------------------------
*MiniStarter.set_query()*
`MiniStarter.set_query`({query}, {buf_id})
Set current query
Parameters ~
{query} `(string|nil)` Query to be set (only if it results into at least one
active item). Default: `nil` for setting query to empty string, which
essentially resets query.
{buf_id} `(number|nil)` Buffer identifier of a valid Starter buffer.
Default: current buffer.
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -0,0 +1,367 @@
*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.
------------------------------------------------------------------------------
*MiniStatusline-example-content*
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`.
------------------------------------------------------------------------------
*MiniStatusline.setup()*
`MiniStatusline.setup`({config})
Module setup
Parameters ~
{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.config*
`MiniStatusline.config`
Module config
Default values:
>lua
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,
}
<
------------------------------------------------------------------------------
*MiniStatusline.active()*
`MiniStatusline.active`()
Compute content for active window
------------------------------------------------------------------------------
*MiniStatusline.inactive()*
`MiniStatusline.inactive`()
Compute content for inactive window
------------------------------------------------------------------------------
*MiniStatusline.combine_groups()*
`MiniStatusline.combine_groups`({groups})
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).
Parameters ~
{groups} `(table)` Array of groups.
Return ~
`(string)` String suitable for 'statusline'.
------------------------------------------------------------------------------
*MiniStatusline.is_truncated()*
`MiniStatusline.is_truncated`({trunc_width})
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.
Parameters ~
{trunc_width} `(number|nil)` Truncation width. If `nil`, output is `false`.
Return ~
`(boolean)` Whether to truncate.
------------------------------------------------------------------------------
*MiniStatusline.section_mode()*
`MiniStatusline.section_mode`({args})
Section for Vim |mode()|
Short output is returned if window width is lower than `args.trunc_width`.
Parameters ~
{args} `(table)` Section arguments.
Return ~
`(...)` Section string and mode's highlight group.
------------------------------------------------------------------------------
*MiniStatusline.section_git()*
`MiniStatusline.section_git`({args})
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`.
Parameters ~
{args} `(table)` Section arguments. Use `args.icon` to supply your own icon.
Return ~
`(string)` Section string.
------------------------------------------------------------------------------
*MiniStatusline.section_diff()*
`MiniStatusline.section_diff`({args})
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`.
Parameters ~
{args} `(table)` Section arguments. Use `args.icon` to supply your own icon.
Return ~
`(string)` Section string.
------------------------------------------------------------------------------
*MiniStatusline.section_diagnostics()*
`MiniStatusline.section_diagnostics`({args})
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`.
Parameters ~
{args} `(table)` Section arguments. 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 ~
`(string)` Section string.
------------------------------------------------------------------------------
*MiniStatusline.section_lsp()*
`MiniStatusline.section_lsp`({args})
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`.
Parameters ~
{args} `(table)` Section arguments. Use `args.icon` to supply your own icon.
Return ~
`(string)` Section string.
------------------------------------------------------------------------------
*MiniStatusline.section_filename()*
`MiniStatusline.section_filename`({args})
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`.
Parameters ~
{args} `(table)` Section arguments.
Return ~
`(string)` Section string.
------------------------------------------------------------------------------
*MiniStatusline.section_fileinfo()*
`MiniStatusline.section_fileinfo`({args})
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.
Parameters ~
{args} `(table)` Section arguments.
Return ~
`(string)` Section string.
------------------------------------------------------------------------------
*MiniStatusline.section_location()*
`MiniStatusline.section_location`({args})
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`.
Parameters ~
{args} `(table)` Section arguments.
Return ~
`(string)` Section string.
------------------------------------------------------------------------------
*MiniStatusline.section_searchcount()*
`MiniStatusline.section_searchcount`({args})
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 }`.
Parameters ~
{args} `(table)` Section arguments.
Return ~
`(string)` Section string.
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -0,0 +1,850 @@
*mini.surround* Surround actions
*MiniSurround*
MIT License Copyright (c) 2021 Evgeni Chasnovski
==============================================================================
Fast and feature-rich surrounding. Can be configured to have experience
similar to 'tpope/vim-surround' (see |MiniSurround-vim-surround-config|).
Features:
- Actions (all of them are dot-repeatable out of the box and respect
|[count]|) with configurable keymappings:
- Add surrounding with `sa` (in visual mode or on motion).
- Delete surrounding with `sd`.
- Replace surrounding with `sr`.
- Find surrounding with `sf` or `sF` (move cursor right or left).
- Highlight surrounding with `sh`.
- Change number of neighbor lines with `sn` (see |MiniSurround-algorithm|).
- Surrounding is identified by a single character as both "input" (in
`delete` and `replace` start, `find`, and `highlight`) and "output" (in
`add` and `replace` end):
- 'f' - function call (string of alphanumeric symbols or '_' or '.'
followed by balanced '()'). In "input" finds function call, in
"output" prompts user to enter function name.
- 't' - tag. In "input" finds tag with same identifier, in "output"
prompts user to enter tag name with possible attributes.
- All symbols in brackets '()', '[]', '{}', '<>". In "input' represents
balanced brackets (open - with whitespace pad, close - without), in
"output" - left and right parts of brackets.
- '?' - interactive. Prompts user to enter left and right parts.
- All other alphanumeric, punctuation, or space characters represent
surrounding with identical left and right parts.
- Configurable search methods to find not only covering but possibly next,
previous, or nearest surrounding. See more in |MiniSurround.config|.
- All actions involving finding surrounding (delete, replace, find,
highlight) can be used with suffix that changes search method to find
previous/last. See more in |MiniSurround.config|.
Known issues which won't be resolved:
- Search for surrounding is done using Lua patterns (regex-like approach).
So certain amount of false positives should be expected.
- When searching for "input" surrounding, there is no distinction if it is
inside string or comment. So in this case there will be not proper match
for a function call: 'f(a = ")", b = 1)'.
- Tags are searched using regex-like methods, so issues are inevitable.
Overall it is pretty good, but certain cases won't work. Like self-nested
tags won't match correctly on both ends: '<a><a></a></a>'.
# Setup ~
This module needs a setup with `require('mini.surround').setup({})`
(replace `{}` with your `config` table). It will create global Lua table
`MiniSurround` which you can use for scripting or manually (with
`:lua MiniSurround.*`).
See |MiniSurround.config| for `config` structure and default values.
You can override runtime config settings locally to buffer inside
`vim.b.minisurround_config` which should have same structure as
`MiniSurround.config`. See |mini.nvim-buffer-local-config| for more details.
To stop module from showing non-error feedback, set `config.silent = true`.
# Example usage ~
Regular mappings:
- `saiw)` - add (`sa`) for inner word (`iw`) parenthesis (`)`).
- `saiw?[[<CR>]]<CR>` - add (`sa`) for inner word (`iw`) interactive
surrounding (`?`): `[[` for left and `]]` for right.
- `2sdf` - delete (`sd`) second (`2`) surrounding function call (`f`).
- `sr)tdiv<CR>` - replace (`sr`) surrounding parenthesis (`)`) with tag
(`t`) with identifier 'div' (`div<CR>` in command line prompt).
- `sff` - find right (`sf`) part of surrounding function call (`f`).
- `sh}` - highlight (`sh`) for a brief period of time surrounding curly
brackets (`}`).
Extended mappings (temporary force "prev"/"next" search methods):
- `sdnf` - delete (`sd`) next (`n`) function call (`f`).
- `srlf(` - replace (`sr`) last (`l`) function call (`f`) with padded
bracket (`(`).
- `2sfnt` - find (`sf`) second (`2`) next (`n`) tag (`t`).
- `2shl}` - highlight (`sh`) last (`l`) second (`2`) curly bracket (`}`).
# Comparisons ~
- 'tpope/vim-surround':
- 'vim-surround' has completely different, with other focus set of
default mappings, while 'mini.surround' has a more coherent set.
- 'mini.surround' supports dot-repeat, customized search path (see
|MiniSurround.config|), customized specifications (see
|MiniSurround-surround-specification|) allowing usage of tree-sitter
queries (see |MiniSurround.gen_spec.input.treesitter()|),
highlighting and finding surrounding, "last"/"next" extended
mappings. While 'vim-surround' does not.
- 'machakann/vim-sandwich':
- Both have same keybindings for common actions (add, delete, replace).
- Otherwise same differences as with 'tpop/vim-surround' (except
dot-repeat because 'vim-sandwich' supports it).
- 'kylechui/nvim-surround':
- 'nvim-surround' is designed after 'tpope/vim-surround' with same
default mappings and logic, while 'mini.surround' has mappings
similar to 'machakann/vim-sandwich'.
- 'mini.surround' has more flexible customization of input surrounding
(with composed patterns, region pair(s), search methods).
- 'mini.surround' supports |[count]| in both input and output
surrounding (see |MiniSurround-count|) while 'nvim-surround' doesn't.
- 'mini.surround' supports "last"/"next" extended mappings.
- |mini.ai|:
- Both use similar logic for finding target: textobject in 'mini.ai'
and surrounding pair in 'mini.surround'. While 'mini.ai' uses
extraction pattern for separate `a` and `i` textobjects,
'mini.surround' uses it to select left and right surroundings
(basically a difference between `a` and `i` textobjects).
- Some builtin specifications are slightly different:
- Quotes in 'mini.ai' are balanced, in 'mini.surround' they are not.
- The 'mini.surround' doesn't have argument surrounding.
- Default behavior in 'mini.ai' selects one of the edges into `a`
textobject, while 'mini.surround' - both.
# Highlight groups ~
* `MiniSurround` - highlighting of requested surrounding.
To change any highlight group, modify it directly with |:highlight|.
# Disabling ~
To disable, set `vim.g.minisurround_disable` (globally) or
`vim.b.minisurround_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.
------------------------------------------------------------------------------
*MiniSurround-surround-builtin*
Builtin surroundings ~
This table describes all builtin surroundings along with what they
represent. Explanation:
- `Key` represents the surrounding identifier: single character which should
be typed after action mappings (see |MiniSurround.config.mappings|).
- `Name` is a description of surrounding.
- `Example line` contains a string for which examples are constructed. The
`*` denotes the cursor position over `a` character.
- `Delete` shows the result of typing `sd` followed by surrounding identifier.
It aims to demonstrate "input" surrounding which is also used in replace
with `sr` (surrounding id is typed first), highlight with `sh`, find with
`sf` and `sF`.
- `Replace` shows the result of typing `sr!` followed by surrounding
identifier (with possible follow up from user). It aims to demonstrate
"output" surrounding which is also used in adding with `sa` (followed by
textobject/motion or in Visual mode).
Example: typing `sd)` with cursor on `*` (covers `a` character) changes line
`!( *a (bb) )!` into `! aa (bb) !`. Typing `sr!)` changes same initial line
into `(( aa (bb) ))`.
>
|Key| Name | Example line | Delete | Replace |
|---|---------------|---------------|-------------|-----------------|
| ( | Balanced () | !( *a (bb) )! | !aa (bb)! | ( ( aa (bb) ) ) |
| [ | Balanced [] | ![ *a [bb] ]! | !aa [bb]! | [ [ aa [bb] ] ] |
| { | Balanced {} | !{ *a {bb} }! | !aa {bb}! | { { aa {bb} } } |
| < | Balanced <> | !< *a <bb> >! | !aa <bb>! | < < aa <bb> > > |
|---|---------------|---------------|-------------|-----------------|
| ) | Balanced () | !( *a (bb) )! | ! aa (bb) ! | (( aa (bb) )) |
| ] | Balanced [] | ![ *a [bb] ]! | ! aa [bb] ! | [[ aa [bb] ]] |
| } | Balanced {} | !{ *a {bb} }! | ! aa {bb} ! | {{ aa {bb} }} |
| > | Balanced <> | !< *a <bb> >! | ! aa <bb> ! | << aa <bb> >> |
| b | Alias for | !( *a {bb} )! | ! aa {bb} ! | (( aa {bb} )) |
| | ), ], or } | | | |
|---|---------------|---------------|-------------|-----------------|
| q | Alias for | !'aa'*a'aa'! | !'aaaaaa'! | "'aa'aa'aa'" |
| | ", ', or ` | | | |
|---|---------------|---------------|-------------|-----------------|
| ? | User prompt | !e * o! | ! a ! | ee a oo |
| |(typed e and o)| | | |
|---|---------------|---------------|-------------|-----------------|
| t | Tag | !<x>*</x>! | !a! | <y><x>a</x></y> |
| | | | | (typed y) |
|---|---------------|---------------|-------------|-----------------|
| f | Function call | !f(*a, bb)! | !aa, bb! | g(f(*a, bb)) |
| | | | | (typed g) |
|---|---------------|---------------|-------------|-----------------|
| | Default | !_a*a_! | !aaa! | __aaa__ |
| | (typed _) | | | |
|---|---------------|---------------|-------------|-----------------|
<
Notes:
- All examples assume default `config.search_method`.
- Open brackets differ from close brackets by how they treat inner edge
whitespace: open includes it left and right parts, close does not.
- Output value of `b` alias is same as `)`. For `q` alias - same as `"`.
- Default surrounding is activated for all characters which are not
configured surrounding identifiers. Notes:
- Due to special handling of underlying `x.-x` Lua pattern
(see |MiniSurround-search-algorithm|), it doesn't really support
non-trivial `[count]` for "cover" search method.
- When cursor is exactly on the identifier character while there are
two matching candidates on both left and right, the one resulting in
region with smaller width is preferred.
------------------------------------------------------------------------------
*MiniSurround-glossary*
Note: this is similar to |MiniAi-glossary|.
- REGION - table representing region in a buffer. Fields: <from> and
<to> for inclusive start and end positions (<to> might be `nil` to
describe empty region). Each position is also a table with line <line>
and column <col> (both start at 1). Examples: >lua
{ from = { line = 1, col = 1 }, to = { line = 2, col = 1 } }
-- Empty region
{ from = { line = 10, col = 10 } }
<
- REGION PAIR - table representing regions for left and right surroundings.
Fields: <left> and <right> with regions. Examples: >lua
{
left = { from = { line = 1, col = 1 }, to = { line = 1, col = 1 } },
right = { from = { line = 1, col = 3 } },
}
<
- PATTERN - string describing Lua pattern.
- SPAN - interval inside a string (end-exclusive). Like [1, 5). Equal
`from` and `to` edges describe empty span at that point.
- SPAN `A = [a1, a2)` COVERS `B = [b1, b2)` if every element of
`B` is within `A` (`a1 <= b < a2`).
It also is described as B IS NESTED INSIDE A.
- NESTED PATTERN - array of patterns aimed to describe nested spans.
- SPAN MATCHES NESTED PATTERN if there is a sequence of consecutively
nested spans each matching corresponding pattern within substring of
previous span (or input string for first span). Example: >lua
-- Nested patterns for balanced `()` with inner space
{ '%b()', '^. .* .$' }
-- Example input string (with columns underneath for easier reading):
"( ( () ( ) ) )"
-- 12345678901234
<
Here are all matching spans [1, 15) and [3, 13). Both [5, 7) and [8, 10)
match first pattern but not second. All other combinations of `(` and `)`
don't match first pattern (not balanced).
- COMPOSED PATTERN: array with each element describing possible pattern
(or array of them) at that place. Composed pattern basically defines all
possible combinations of nested pattern (their cartesian product).
Examples:
1. Either balanced `()` or balanced `[]` but both with inner edge space: >lua
-- Composed pattern
{ { '%b()', '%b[]' }, '^. .* .$' }
-- Composed pattern expanded into equivalent array of nested patterns
{ '%b()', '^. .* .$' } -- and
{ '%b[]', '^. .* .$' }
<
2. Either "balanced `()` with inner edge space" or "balanced `[]` with
no inner edge space", both with 5 or more characters: >lua
-- Composed pattern
{ { { '%b()', '^. .* .$' }, { '%b[]', '^.[^ ].*[^ ].$' } }, '.....' }
-- Composed pattern expanded into equivalent array of nested patterns
{ '%b()', '^. .* .$', '.....' } -- and
{ '%b[]', '^.[^ ].*[^ ].$', '.....' }
<
- SPAN MATCHES COMPOSED PATTERN if it matches at least one nested pattern
from expanded composed pattern.
------------------------------------------------------------------------------
*MiniSurround-surround-specification*
Surround specification is a table with keys:
- <input> - defines how to find and extract surrounding for "input"
operations (like `delete`). See more in "Input surrounding" section.
- <output> - defines what to add on left and right for "output" operations
(like `add`). See more in "Output surrounding" section.
Example of surround info for builtin `)` identifier: >lua
{
input = { '%b()', '^.().*().$' },
output = { left = '(', right = ')' }
}
<
# Input surrounding ~
Specification for input surrounding has a structure of composed pattern
(see |MiniSurround-glossary|) with two differences:
- Last pattern(s) should have two or four empty capture groups denoting
how the last string should be processed to extract surrounding parts:
- Two captures represent left part from start of string to first
capture and right part - from second capture to end of string.
Example: `a()b()c` defines left surrounding as 'a', right - 'c'.
- Four captures define left part inside captures 1 and 2, right part -
inside captures 3 and 4. Example: `a()()b()c()` defines left part as
empty, right part as 'c'.
- Allows callable objects (see |vim.is_callable()|) in certain places
(enables more complex surroundings in exchange of increase in configuration
complexity and computations):
- If specification itself is a callable, it will be called without
arguments and should return one of:
- Composed pattern. Useful for implementing user input. Example of
simplified variant of input surrounding for function call with
name taken from user prompt: >lua
function()
local left_edge = vim.pesc(vim.fn.input('Function name: '))
return { left_edge .. '%b()', '^.-%(().*()%)$' }
end
<
- Single region pair (see |MiniSurround-glossary|). Useful to allow
full control over surrounding. Will be taken as is. Example of
returning first and last lines of a buffer: >lua
function()
local n_lines = vim.fn.line('$')
return {
left = {
from = { line = 1, col = 1 },
to = { line = 1, col = vim.fn.getline(1):len() }
},
right = {
from = { line = n_lines, col = 1 },
to = { line = n_lines, col = vim.fn.getline(n_lines):len() }
},
}
end
<
- Array of region pairs. Useful for incorporating other instruments,
like treesitter (see |MiniSurround.gen_spec.treesitter()|). The
best region pair will be picked in the same manner as with composed
pattern (respecting options `n_lines`, `search_method`, etc.) using
output region (from start of left region to end of right region).
Example using edges of "best" line with display width more than 80: >lua
function()
local make_line_region_pair = function(n)
local left = { line = n, col = 1 }
local right = { line = n, col = vim.fn.getline(n):len() }
return {
left = { from = left, to = left },
right = { from = right, to = right },
}
end
local res = {}
for i = 1, vim.fn.line('$') do
if vim.fn.getline(i):len() > 80 then
table.insert(res, make_line_region_pair(i))
end
end
return res
end
<
- If there is a callable instead of assumed string pattern, it is expected
to have signature `(line, init)` and behave like `pattern:find()`.
It should return two numbers representing span in `line` next after
or at `init` (`nil` if there is no such span).
!IMPORTANT NOTE!: it means that output's `from` shouldn't be strictly
to the left of `init` (it will lead to infinite loop). Not allowed as
last item (as it should be pattern with captures).
Example of matching only balanced parenthesis with big enough width: >lua
{
'%b()',
function(s, init)
if init > 1 or s:len() < 5 then return end
return 1, s:len()
end,
'^.().*().$'
}
<
More examples: >lua
-- Pair of balanced brackets from set (used for builtin `b` identifier)
{ { '%b()', '%b[]', '%b{}' }, '^.().*().$' }
-- Lua block string
{ '%[%[().-()%]%]' }
<
See |MiniSurround.gen_spec| for function wrappers to create commonly used
surrounding specifications.
# Output surrounding ~
Specification for output can be either a table with <left> and <right> fields,
or a callable returning such table (will be called with no arguments).
Strings can contain new lines character "\n" to add multiline parts.
Examples: >lua
-- Lua block string
{ left = '[[', right = ']]' }
-- Brackets on separate lines (indentation is not preserved)
{ left = '(\n', right = '\n)' }
-- Function call
function()
local function_name = MiniSurround.user_input('Function name')
return { left = function_name .. '(', right = ')' }
end
<
------------------------------------------------------------------------------
*MiniSurround-count*
Count with actions
|[count]| is supported by all actions in the following ways:
- In add, two types of `[count]` is supported in Normal mode:
`[count1]sa[count2][textobject]`. The `[count1]` defines how many times
left and right parts of output surrounding will be repeated and `[count2]` is
used for textobject.
In Visual mode `[count]` is treated as `[count1]`.
Example: `2sa3aw)` and `v3aw2sa)` will result into textobject `3aw` being
surrounded by `((` and `))`.
- In delete/replace/find/highlight `[count]` means "find n-th surrounding
and execute operator on it".
Example: `2sd)` on line `(a(b(c)b)a)` with cursor on `c` will result into
`(ab(c)ba)` (and not in `(abcba)` if it would have meant "delete n times").
------------------------------------------------------------------------------
*MiniSurround-search-algorithm*
Search algorithm design
Search for the input surrounding relies on these principles:
- Input surrounding specification is constructed based on surrounding
identifier (see |MiniSurround-surround-specification|).
- General search is done by converting some 2d buffer region (neighborhood
of reference region) into 1d string (each line is appended with `\n`).
Then search for a best span matching specification is done inside string
(see |MiniSurround-glossary|). After that, span is converted back into 2d
region. Note: first search is done inside reference region lines, and
only after that - inside its neighborhood within `config.n_lines` (see
|MiniSurround.config|).
- The best matching span is chosen by iterating over all spans matching
surrounding specification and comparing them with "current best".
Comparison also depends on reference region (tighter covering is better,
otherwise closer is better) and search method (if span is even considered).
- Extract pair of spans (for left and right regions in region pair) based
on extraction pattern (last item in nested pattern).
- For |[count]| greater than 1, steps are repeated with current best match
becoming reference region. One such additional step is also done if final
region is equal to reference region.
Notes:
- Iteration over all matched spans is done in depth-first fashion with
respect to nested pattern.
- It is guaranteed that span is compared only once.
- For the sake of increasing functionality, during iteration over all
matching spans, some Lua patterns in composed pattern are handled
specially.
- `%bxx` (`xx` is two identical characters). It denotes balanced pair
of identical characters and results into "paired" matches. For
example, `%b""` for `"aa" "bb"` would match `"aa"` and `"bb"`, but
not middle `" "`.
- `x.-y` (`x` and `y` are different strings). It results only in matches with
smallest width. For example, `e.-o` for `e e o o` will result only in
middle `e o`. Note: it has some implications for when parts have
quantifiers (like `+`, etc.), which usually can be resolved with
frontier pattern `%f[]`.
------------------------------------------------------------------------------
*MiniSurround.setup()*
`MiniSurround.setup`({config})
Module setup
Parameters ~
{config} `(table|nil)` Module config table. See |MiniSurround.config|.
Usage ~
>lua
require('mini.surround').setup() -- use default config
-- OR
require('mini.surround').setup({}) -- replace {} with your config table
<
------------------------------------------------------------------------------
*MiniSurround.config*
`MiniSurround.config`
Module config
Default values:
>lua
MiniSurround.config = {
-- Add custom surroundings to be used on top of builtin ones. For more
-- information with examples, see `:h MiniSurround.config`.
custom_surroundings = nil,
-- Duration (in ms) of highlight when calling `MiniSurround.highlight()`
highlight_duration = 500,
-- Module mappings. Use `''` (empty string) to disable one.
mappings = {
add = 'sa', -- Add surrounding in Normal and Visual modes
delete = 'sd', -- Delete surrounding
find = 'sf', -- Find surrounding (to the right)
find_left = 'sF', -- Find surrounding (to the left)
highlight = 'sh', -- Highlight surrounding
replace = 'sr', -- Replace surrounding
update_n_lines = 'sn', -- Update `n_lines`
suffix_last = 'l', -- Suffix to search with "prev" method
suffix_next = 'n', -- Suffix to search with "next" method
},
-- Number of lines within which surrounding is searched
n_lines = 20,
-- Whether to respect selection type:
-- - Place surroundings on separate lines in linewise mode.
-- - Place surroundings on each line in blockwise mode.
respect_selection_type = false,
-- How to search for surrounding (first inside current line, then inside
-- neighborhood). One of 'cover', 'cover_or_next', 'cover_or_prev',
-- 'cover_or_nearest', 'next', 'prev', 'nearest'. For more details,
-- see `:h MiniSurround.config`.
search_method = 'cover',
-- Whether to disable showing non-error feedback
silent = false,
}
<
*MiniSurround-vim-surround-config*
# Setup similar to 'tpope/vim-surround' ~
This module is primarily designed after 'machakann/vim-sandwich'. To get
behavior closest to 'tpope/vim-surround' (but not identical), use this setup: >lua
require('mini.surround').setup({
mappings = {
add = 'ys',
delete = 'ds',
find = '',
find_left = '',
highlight = '',
replace = 'cs',
update_n_lines = '',
-- Add this only if you don't want to use extended mappings
suffix_last = '',
suffix_next = '',
},
search_method = 'cover_or_next',
})
-- Remap adding surrounding to Visual mode selection
vim.keymap.del('x', 'ys')
vim.keymap.set('x', 'S', [[:<C-u>lua MiniSurround.add('visual')<CR>]], { silent = true })
-- Make special mapping for "add surrounding for line"
vim.keymap.set('n', 'yss', 'ys_', { remap = true })
<
# Options ~
## Mappings ~
`config.mappings` defines what mappings are set up for particular actions.
By default it uses "prefix style" left hand side starting with "s" (for
"surround"): `sa` - "surround add", `sd` - "surround delete", etc.
Note: if 'timeoutlen' is low enough to cause occasional usage of |s| key
(that deletes character under cursor), disable it with the following call: >lua
vim.keymap.set({ 'n', 'x' }, 's', '<Nop>')
<
## Custom surroundings ~
User can define own surroundings by supplying `config.custom_surroundings`.
It should be a **table** with keys being single character surrounding
identifier and values - surround specification (see
|MiniSurround-surround-specification|).
General recommendations:
- In `config.custom_surroundings` only some data can be defined (like only
`output`). Other fields will be taken from builtin surroundings.
- Function returning surround info at <input> or <output> fields of
specification is helpful when user input is needed (like asking for
function name). Use |input()| or |MiniSurround.user_input()|. Return
`nil` to stop any current surround operation.
Examples of using `config.custom_surroundings` (see more examples at
|MiniSurround.gen_spec|): >lua
local surround = require('mini.surround')
surround.setup({
custom_surroundings = {
-- Make `)` insert parts with spaces. `input` pattern stays the same.
[')'] = { output = { left = '( ', right = ' )' } },
-- Use function to compute surrounding info
['*'] = {
input = function()
local n_star = MiniSurround.user_input('Number of * to find')
local many_star = string.rep('%*', tonumber(n_star) or 1)
return { many_star .. '().-()' .. many_star }
end,
output = function()
local n_star = MiniSurround.user_input('Number of * to output')
local many_star = string.rep('*', tonumber(n_star) or 1)
return { left = many_star, right = many_star }
end,
},
},
})
-- Create custom surrounding for Lua's block string `[[...]]`
-- Use this inside autocommand or 'after/ftplugin/lua.lua' file
vim.b.minisurround_config = {
custom_surroundings = {
s = {
input = { '%[%[().-()%]%]' },
output = { left = '[[', right = ']]' },
},
},
}
<
## Respect selection type ~
Boolean option `config.respect_selection_type` controls whether to respect
selection type when adding and deleting surrounding. When enabled:
- Linewise adding places surroundings on separate lines while indenting
surrounded lines ones.
- Deleting surroundings which look like they were the result of linewise
adding will act to revert it: delete lines with surroundings and dedent
surrounded lines ones.
- Blockwise adding places surroundings on whole edges, not only start and
end of selection. Note: it doesn't really work outside of text and in
presence of multibyte characters; and probably won't due to
implementation difficulties.
## Search method ~
Value of `config.search_method` defines how best match search is done.
Based on its value, one of the following matches will be selected:
- Covering match. Left/right edge is before/after left/right edge of
reference region.
- Previous match. Left/right edge is before left/right edge of reference
region.
- Next match. Left/right edge is after left/right edge of reference region.
- Nearest match. Whichever is closest among previous and next matches.
Possible values are:
- `'cover'` (default) - use only covering match. Don't use either previous or
next; report that there is no surrounding found.
- `'cover_or_next'` - use covering match. If not found, use next.
- `'cover_or_prev'` - use covering match. If not found, use previous.
- `'cover_or_nearest'` - use covering match. If not found, use nearest.
- `'next'` - use next match.
- `'previous'` - use previous match.
- `'nearest'` - use nearest match.
Note: search is first performed on the reference region lines and only
after failure - on the whole neighborhood defined by `config.n_lines`. This
means that with `config.search_method` not equal to `'cover'`, "previous"
or "next" surrounding will end up as search result if they are found on
first stage although covering match might be found in bigger, whole
neighborhood. This design is based on observation that most of the time
operation is done within reference region lines (usually cursor line).
Here is an example of how replacing `)` with `]` surrounding is done based
on a value of `'config.search_method'` when cursor is inside `bbb` word:
- `'cover'`: `(a) bbb (c)` -> `(a) bbb (c)` (with message)
- `'cover_or_next'`: `(a) bbb (c)` -> `(a) bbb [c]`
- `'cover_or_prev'`: `(a) bbb (c)` -> `[a] bbb (c)`
- `'cover_or_nearest'`: depends on cursor position.
For first and second `b` - as in `cover_or_prev` (as previous match is
nearer), for third - as in `cover_or_next` (as next match is nearer).
- `'next'`: `(a) bbb (c)` -> `(a) bbb [c]`. Same outcome for `(bbb)`.
- `'prev'`: `(a) bbb (c)` -> `[a] bbb (c)`. Same outcome for `(bbb)`.
- `'nearest'`: depends on cursor position (same as in `'cover_or_nearest'`).
## Search suffixes ~
To provide more searching possibilities, 'mini.surround' creates extended
mappings force "prev" and "next" methods for particular search. It does so
by appending mapping with certain suffix: `config.mappings.suffix_last` for
mappings which will use "prev" search method, `config.mappings.suffix_next`
- "next" search method.
Notes:
- It creates new mappings only for actions involving surrounding search:
delete, replace, find (right and left), highlight.
- All new mappings behave the same way as if `config.search_method` is set
to certain search method. They are dot-repeatable, respect |[count]|, etc.
- Supply empty string to disable creation of corresponding set of mappings.
Example with default values (`n` for `suffix_next`, `l` for `suffix_last`)
and initial line `(aa) (bb) (cc)`.
- Typing `sdn)` with cursor inside `(aa)` results into `(aa) bb (cc)`.
- Typing `sdl)` with cursor inside `(cc)` results into `(aa) bb (cc)`.
- Typing `2srn)]` with cursor inside `(aa)` results into `(aa) (bb) [cc]`.
------------------------------------------------------------------------------
*MiniSurround.add()*
`MiniSurround.add`({mode})
Add surrounding
No need to use it directly, everything is setup in |MiniSurround.setup|.
Parameters ~
{mode} `(string)` Mapping mode (normal by default).
------------------------------------------------------------------------------
*MiniSurround.delete()*
`MiniSurround.delete`()
Delete surrounding
No need to use it directly, everything is setup in |MiniSurround.setup|.
------------------------------------------------------------------------------
*MiniSurround.replace()*
`MiniSurround.replace`()
Replace surrounding
No need to use it directly, everything is setup in |MiniSurround.setup|.
------------------------------------------------------------------------------
*MiniSurround.find()*
`MiniSurround.find`()
Find surrounding
No need to use it directly, everything is setup in |MiniSurround.setup|.
------------------------------------------------------------------------------
*MiniSurround.highlight()*
`MiniSurround.highlight`()
Highlight surrounding
No need to use it directly, everything is setup in |MiniSurround.setup|.
------------------------------------------------------------------------------
*MiniSurround.update_n_lines()*
`MiniSurround.update_n_lines`()
Update `MiniSurround.config.n_lines`
Convenient wrapper for updating `MiniSurround.config.n_lines` in case the
default one is not appropriate.
------------------------------------------------------------------------------
*MiniSurround.user_input()*
`MiniSurround.user_input`({prompt}, {text})
Ask user for input
This is mainly a wrapper for |input()| which allows empty string as input,
cancelling with `<Esc>` and `<C-c>`, and slightly modifies prompt. Use it
to ask for input inside function custom surrounding (see |MiniSurround.config|).
------------------------------------------------------------------------------
*MiniSurround.gen_spec*
`MiniSurround.gen_spec`
Generate common surrounding specifications
This is a table with two sets of generator functions: <input> and <output>
(currently empty). Each is a table with function values generating
corresponding surrounding specification.
See also ~
|MiniAi.gen_spec|
------------------------------------------------------------------------------
*MiniSurround.gen_spec.input.treesitter()*
`MiniSurround.gen_spec.input.treesitter`({captures}, {opts})
Treesitter specification for input surrounding
This is a specification in function form. When called with a pair of
treesitter captures, it returns a specification function outputting an
array of region pairs derived from <outer> and <inner> captures. It first
searches for all matched nodes of outer capture and then completes each one
with the biggest match of inner capture inside that node (if any). The result
region pair is a difference between regions of outer and inner captures.
In order for this to work, apart from working treesitter parser for desired
language, user should have a reachable language-specific 'textobjects'
query (see |vim.treesitter.query.get()| or |get_query()|, depending on your
Neovim version).
The most straightforward way for this is to have 'textobjects.scm' query
file with treesitter captures stored in some recognized path. This is
primarily designed to be compatible with plugin
'nvim-treesitter/nvim-treesitter-textobjects', but can be used without it.
Two most common approaches for having a query file:
- Install 'nvim-treesitter/nvim-treesitter-textobjects'. It has curated and
well maintained builtin query files for many languages with a standardized
capture names, like `call.outer`, `call.inner`, etc.
- Manually create file 'after/queries/<language name>/textobjects.scm' in
your |$XDG_CONFIG_HOME| directory. It should contain queries with
captures (later used to define surrounding parts). See |lua-treesitter-query|.
To verify that query file is reachable, run (example for "lua" language,
output should have at least an intended file): >vim
:lua print(vim.inspect(vim.treesitter.query.get_files('lua','textobjects')))
<
Example configuration for function definition textobject with
'nvim-treesitter/nvim-treesitter-textobjects' captures: >lua
local ts_input = require('mini.surround').gen_spec.input.treesitter
require('mini.surround').setup({
custom_surroundings = {
-- Use tree-sitter to search for function call
f = {
input = ts_input({ outer = '@call.outer', inner = '@call.inner' })
},
}
})
<
Notes:
- By default query is done using 'nvim-treesitter' plugin if it is present
(falls back to builtin methods otherwise). This allows for a more
advanced features (like multiple buffer languages, custom directives, etc.).
See `opts.use_nvim_treesitter` for how to disable this.
- It uses buffer's |filetype| to determine query language.
- On large files it is slower than pattern-based textobjects. Still very
fast though (one search should be magnitude of milliseconds or tens of
milliseconds on really large file).
Parameters ~
{captures} `(table)` Captures for outer and inner parts of region pair:
table with <outer> and <inner> fields with captures for outer
(`[left.form; right.to]`) and inner (`(left.to; right.from)` both edges
exclusive, i.e. they won't be a part of surrounding) regions. Each value
should be a string capture starting with `'@'`.
{opts} `(table|nil)` Options. Possible values:
- <use_nvim_treesitter> - whether to try to use 'nvim-treesitter' plugin
(if present) to do the query. It implements more advanced behavior at
cost of increased execution time. Provides more coherent experience if
'nvim-treesitter-textobjects' queries are used. Default: `true`.
Return ~
`(function)` Function which returns array of current buffer region pairs
representing differences between outer and inner captures.
See also ~
|MiniSurround-surround-specification| for how this type of
surrounding specification is processed.
|get_query()| for how query is fetched in case of no 'nvim-treesitter'.
|Query:iter_captures()| for how all query captures are iterated in case of
no 'nvim-treesitter'.
|MiniAi.gen_spec.treesitter()| for similar 'mini.ai' generator.
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -0,0 +1,144 @@
*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.
------------------------------------------------------------------------------
*MiniTabline.setup()*
`MiniTabline.setup`({config})
Module setup
Parameters ~
{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.config*
`MiniTabline.config`
Module config
Default values:
>lua
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',
}
<
# 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.make_tabline_string()*
`MiniTabline.make_tabline_string`()
Make string for |tabline|
------------------------------------------------------------------------------
*MiniTabline.default_format()*
`MiniTabline.default_format`({buf_id}, {label})
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|.
Parameters ~
{buf_id} `(number)` Buffer identifier.
{label} `(string)` Pre-computed label.
Return ~
`(string)` Formatted label.
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -0,0 +1,911 @@
*mini.test* Test Neovim plugins
*MiniTest*
MIT License Copyright (c) 2022 Evgeni Chasnovski
==============================================================================
Features:
- Test action is defined as a named callable entry of a table.
- Helper for creating child Neovim process which is designed to be used in
tests (including taking and verifying screenshots). See
|MiniTest.new_child_neovim()| and |Minitest.expect.reference_screenshot()|.
- Hierarchical organization of tests with custom hooks, parametrization,
and user data. See |MiniTest.new_set()|.
- Emulation of 'Olivine-Labs/busted' interface (`describe`, `it`, etc.).
- Predefined small yet usable set of expectations (`assert`-like functions).
See |MiniTest.expect|.
- Customizable definition of what files should be tested.
- Test case filtering. There are predefined wrappers for testing a file
(|MiniTest.run_file()|) and case at a location like current cursor position
(|MiniTest.run_at_location()|).
- Customizable reporter of output results. There are two predefined ones:
- |MiniTest.gen_reporter.buffer()| for interactive usage.
- |MiniTest.gen_reporter.stdout()| for headless Neovim.
- Customizable project specific testing script.
What it doesn't support:
- Parallel execution. Due to idea of limiting implementation complexity.
- Mocks, stubs, etc. Use child Neovim process and manually override what is
needed. Reset child process it afterwards.
- "Overly specific" expectations. Tests for (no) equality and (absence of)
errors usually cover most of the needs. Adding new expectations is a
subject to weighing its usefulness against additional implementation
complexity. Use |MiniTest.new_expectation()| to create custom ones.
For more information see:
- 'TESTING.md' file for a hands-on introduction based on examples.
- Code of this plugin's tests. Consider it to be an example of intended
way to use 'mini.test' for test organization and creation.
# Workflow
- Organize tests in separate files. Each test file should return a test set
(explicitly or implicitly by using "busted" style functions).
- Write test actions as callable entries of test set. Use child process
inside test actions (see |MiniTest.new_child_neovim()|) and builtin
expectations (see |MiniTest.expect|).
- Run tests. This does two steps:
- *Collect*. This creates single hierarchical test set, flattens into
array of test cases (see |MiniTest-test-case|) while expanding with
parametrization, and possibly filters them.
- *Execute*. This safely calls hooks and main test actions in specified
order while allowing reporting progress in asynchronous fashion.
Detected errors means test case fail; otherwise - pass.
# Setup ~
This module needs a setup with `require('mini.test').setup({})` (replace
`{}` with your `config` table). It will create global Lua table `MiniTest`
which you can use for scripting or manually (with `:lua MiniTest.*`).
See |MiniTest.config| for available config settings.
You can override runtime config settings locally to buffer inside
`vim.b.minitest_config` which should have same structure as `MiniTest.config`.
See |mini.nvim-buffer-local-config| for more details.
To stop module from showing non-error feedback, set `config.silent = true`.
# Comparisons ~
- Testing infrastructure from 'nvim-lua/plenary.nvim':
- Executes each file in separate headless Neovim process with customizable
'init.vim' file. While 'mini.test' executes everything in current
Neovim process encouraging writing tests with help of manually
managed child Neovim process (see |MiniTest.new_child_neovim()|).
- Tests are expected to be written with embedded simplified versions of
'Olivine-Labs/busted' and 'Olivine-Labs/luassert'. While 'mini.test'
uses concepts of test set (see |MiniTest.new_set()|) and test case
(see |MiniTest-test-case|). It also can emulate bigger part of
"busted" framework.
- Has single way of reporting progress (shows result after every case
without summary). While 'mini.test' can have customized reporters
with defaults for interactive and headless usage (provide more
compact and user-friendly summaries).
- Allows parallel execution, while 'mini.test' does not.
- Allows making mocks, stubs, and spies, while 'mini.test' does not in
favor of manually overwriting functionality in child Neovim process.
Although 'mini.test' supports emulation of "busted style" testing, it will
be more stable to use its designed approach of defining tests (with
`MiniTest.new_set()` and explicit table fields). Couple of reasons:
- "Busted" syntax doesn't support full capabilities offered by 'mini.test'.
Mainly it is about parametrization and supplying user data to test sets.
- It is an emulation, not full support. So some subtle things might not
work the way you expect.
Some hints for converting from 'plenary.nvim' tests to 'mini.test':
- Rename files from "***_spec.lua" to "test_***.lua" and put them in
"tests" directory.
- Replace `assert` calls with 'mini.test' expectations. See |MiniTest.expect|.
- Create main test set `T = MiniTest.new_set()` and eventually return it.
- Make new sets (|MiniTest.new_set()|) from `describe` blocks. Convert
`before_each()` and `after_each` to `pre_case` and `post_case` hooks.
- Make test cases from `it` blocks.
# Highlight groups ~
* `MiniTestEmphasis` - emphasis highlighting. By default it is a bold text.
* `MiniTestFail` - highlighting of failed cases. By default it is a bold
text with `vim.g.terminal_color_1` color (red).
* `MiniTestPass` - highlighting of passed cases. By default it is a bold
text with `vim.g.terminal_color_2` color (green).
To change any highlight group, modify it directly with |:highlight|.
# Disabling ~
To disable, set `vim.g.minitest_disable` (globally) or `vim.b.minitest_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.
------------------------------------------------------------------------------
*MiniTest.setup()*
`MiniTest.setup`({config})
Module setup
Parameters ~
{config} `(table|nil)` Module config table. See |MiniTest.config|.
Usage ~
>lua
require('mini.test').setup() -- use default config
-- OR
require('mini.test').setup({}) -- replace {} with your config table
<
------------------------------------------------------------------------------
*MiniTest.config*
`MiniTest.config`
Module config
Default values:
>lua
MiniTest.config = {
-- Options for collection of test cases. See `:h MiniTest.collect()`.
collect = {
-- Temporarily emulate functions from 'busted' testing framework
-- (`describe`, `it`, `before_each`, `after_each`, and more)
emulate_busted = true,
-- Function returning array of file paths to be collected.
-- Default: all Lua files in 'tests' directory starting with 'test_'.
find_files = function()
return vim.fn.globpath('tests', '**/test_*.lua', true, true)
end,
-- Predicate function indicating if test case should be executed
filter_cases = function(case) return true end,
},
-- Options for execution of test cases. See `:h MiniTest.execute()`.
execute = {
-- Table with callable fields `start()`, `update()`, and `finish()`
reporter = nil,
-- Whether to stop execution after first error
stop_on_error = false,
},
-- Path (relative to current directory) to script which handles project
-- specific test running
script_path = 'scripts/minitest.lua',
-- Whether to disable showing non-error feedback
silent = false,
}
<
------------------------------------------------------------------------------
*MiniTest.current*
`MiniTest.current`
Table with information about current state of test execution
Use it to examine result of |MiniTest.execute()|. It is reset at the
beginning of every call.
At least these keys are supported:
- <all_cases> - array with all cases being currently executed. Basically,
an input of `MiniTest.execute()`.
- <case> - currently executed test case. See |MiniTest-test-case|. Use it
to customize execution output (like adding custom notes, etc).
------------------------------------------------------------------------------
*MiniTest.new_set()*
`MiniTest.new_set`({opts}, {tbl})
Create test set
Test set is one of the two fundamental data structures. It is a table that
defines hierarchical test organization as opposed to sequential
organization with |MiniTest-test-case|.
All its elements are one of three categories:
- A callable (object that can be called; function or table with `__call`
metatble entry) is considered to define a test action. It will be called
with "current arguments" (result of all nested `parametrize` values, read
further). If it throws error, test has failed.
- A test set (output of this function) defines nested structure. Its
options during collection (see |MiniTest.collect()|) will be extended
with options of this (parent) test set.
- Any other elements are considered helpers and don't directly participate
in test structure.
Set options allow customization of test collection and execution (more
details in `opts` description):
- `hooks` - table with elements that will be called without arguments at
predefined stages of test execution.
- `parametrize` - array defining different arguments with which main test
actions will be called. Any non-trivial parametrization will lead to
every element (even nested) be "multiplied" and processed with every
element of `parametrize`. This allows handling many different combination
of tests with little effort.
- `data` - table with user data that will be forwarded to cases. Primary
objective is to be used for customized case filtering.
Notes:
- Preferred way of adding elements is by using syntax `T[name] = element`.
This way order of added elements will be preserved. Any other way won't
guarantee any order.
- Supplied options `opts` are stored in `opts` field of metatable
(`getmetatable(set).opts`).
Parameters ~
{opts} `(table|nil)` Allowed options:
- <hooks> - table with fields:
- <pre_once> - executed before first filtered node.
- <pre_case> - executed before each case (even nested).
- <post_case> - executed after each case (even nested).
- <post_once> - executed after last filtered node.
- <parametrize> - array where each element is itself an array of
parameters to be appended to "current parameters" of callable fields.
Note: don't use plain `{}` as it is equivalent to "parametrization into
zero cases", so no cases will be collected from this set. Calling test
actions with no parameters is equivalent to `{{}}` or not supplying
`parametrize` option at all.
- <data> - user data to be forwarded to cases. Can be used for a more
granular filtering.
{tbl} `(table|nil)` Initial test items (possibly nested). Will be executed
without any guarantees on order.
Return ~
`(table)` A single test set.
Usage ~
>lua
-- Use with defaults
T = MiniTest.new_set()
T['works'] = function() MiniTest.expect.equality(1, 1) end
-- Use with custom options. This will result into two actual cases: first
-- will pass, second - fail.
T['nested'] = MiniTest.new_set({
hooks = { pre_case = function() _G.x = 1 end },
parametrize = { { 1 }, { 2 } }
})
T['nested']['works'] = function(x)
MiniTest.expect.equality(_G.x, x)
end
<
------------------------------------------------------------------------------
*MiniTest-test-case*
Test case
An item of sequential test organization, as opposed to hierarchical with
test set (see |MiniTest.new_set()|). It is created as result of test
collection with |MiniTest.collect()| to represent all necessary information
of test execution.
Execution of test case goes by the following rules:
- Call functions in order:
- All elements of `hooks.pre` from first to last without arguments.
- Field `test` with arguments unpacked from `args`.
- All elements of `hooks.post` from first to last without arguments.
- Error in any call gets appended to `exec.fails`, meaning error in any
hook will lead to test fail.
- State (`exec.state`) is changed before every call and after last call.
Class ~
{Test-case}
Fields ~
{args} `(table)` Array of arguments with which `test` will be called.
{data} `(table)` User data: all fields of `opts.data` from nested test sets.
{desc} `(table)` Description: array of fields from nested test sets.
{exec} `(table|nil)` Information about test case execution. Value of `nil` means
that this particular case was not (yet) executed. Has following fields:
- <fails> - array of strings with failing information.
- <notes> - array of strings with non-failing information.
- <state> - state of test execution. One of:
- 'Executing <name of what is being executed>' (during execution).
- 'Pass' (no fails, no notes).
- 'Pass with notes' (no fails, some notes).
- 'Fail' (some fails, no notes).
- 'Fail with notes' (some fails, some notes).
{hooks} `(table)` Hooks to be executed as part of test case. Has fields
<pre> and <post> with arrays to be consecutively executed before and
after execution of `test`.
{test} `(function|table)` Main callable object representing test action.
------------------------------------------------------------------------------
*MiniTest.skip()*
`MiniTest.skip`({msg})
Skip rest of current callable execution
Can be used inside hooks and main test callable of test case. Note: at the
moment implemented as a specially handled type of error.
Parameters ~
{msg} `(string|nil)` Message to be added to current case notes.
------------------------------------------------------------------------------
*MiniTest.add_note()*
`MiniTest.add_note`({msg})
Add note to currently executed test case
Appends `msg` to `exec.notes` field of |MiniTest.current.case|.
Parameters ~
{msg} `(string)` Note to add.
------------------------------------------------------------------------------
*MiniTest.finally()*
`MiniTest.finally`({f})
Register callable execution after current callable
Can be used inside hooks and main test callable of test case.
Parameters ~
{f} `(function|table)` Callable to be executed after current callable is
finished executing (regardless of whether it ended with error or not).
------------------------------------------------------------------------------
*MiniTest.run()*
`MiniTest.run`({opts})
Run tests
- Try executing project specific script at path `opts.script_path`. If
successful (no errors), then stop.
- Collect cases with |MiniTest.collect()| and `opts.collect`.
- Execute collected cases with |MiniTest.execute()| and `opts.execute`.
Parameters ~
{opts} `(table|nil)` Options with structure similar to |MiniTest.config|.
Absent values are inferred from there.
------------------------------------------------------------------------------
*MiniTest.run_file()*
`MiniTest.run_file`({file}, {opts})
Run specific test file
Basically a |MiniTest.run()| wrapper with custom `collect.find_files` option.
Parameters ~
{file} `(string|nil)` Path to test file. By default a path of current buffer.
{opts} `(table|nil)` Options for |MiniTest.run()|.
------------------------------------------------------------------------------
*MiniTest.run_at_location()*
`MiniTest.run_at_location`({location}, {opts})
Run case(s) covering location
Try filtering case(s) covering location, meaning that definition of its
main `test` action (as taken from builtin `debug.getinfo`) is located in
specified file and covers specified line. Note that it can result in
multiple cases if they come from parametrized test set (see `parametrize`
option in |MiniTest.new_set()|).
Basically a |MiniTest.run()| wrapper with custom `collect.find_files` option.
Parameters ~
{location} `(table|nil)` Table with fields <file> (path to file) and <line>
(line number in that file). Default is taken from current cursor position.
------------------------------------------------------------------------------
*MiniTest.collect()*
`MiniTest.collect`({opts})
Collect test cases
Overview of collection process:
- If `opts.emulate_busted` is `true`, temporary make special global
functions (removed at the end of collection). They can be used inside
test files to create hierarchical structure of test cases.
- Source each file from array output of `opts.find_files`. It should output
a test set (see |MiniTest.new_set()|) or `nil` (if "busted" style is used;
test set is created implicitly).
- Combine all test sets into single set with fields equal to its file path.
- Convert from hierarchical test configuration to sequential: from single
test set to array of test cases (see |MiniTest-test-case|). Conversion is
done in the form of "for every table element do: for every `parametrize`
element do: ...". Details:
- If element is a callable, construct test case with it being main
`test` action. Description is appended with key of element in current
test set table. Hooks, arguments, and data are taken from "current
nested" ones. Add case to output array.
- If element is a test set, process it in similar, recursive fashion.
The "current nested" information is expanded:
- `args` is extended with "current element" from `parametrize`.
- `desc` is appended with element key.
- `hooks` are appended to their appropriate places. `*_case` hooks
will be inserted closer to all child cases than hooks from parent
test sets: `pre_case` at end, `post_case` at start.
- `data` is extended via |vim.tbl_deep_extend()|.
- Any other element is not processed.
- Filter array with `opts.filter_cases`. Note that input case doesn't contain
all hooks, as `*_once` hooks will be added after filtration.
- Add `*_once` hooks to appropriate cases.
Parameters ~
{opts} `(table|nil)` Options controlling case collection. Possible fields:
- <emulate_busted> - whether to emulate 'Olivine-Labs/busted' interface.
It emulates these global functions: `describe`, `it`, `setup`, `teardown`,
`before_each`, `after_each`. Use |MiniTest.skip()| instead of `pending()`
and |MiniTest.finally()| instead of `finally`.
- <find_files> - function which when called without arguments returns
array with file paths. Each file should be a Lua file returning single
test set or `nil`.
- <filter_cases> - function which when called with single test case
(see |MiniTest-test-case|) returns `false` if this case should be filtered
out; `true` otherwise.
Return ~
`(table)` Array of test cases ready to be used by |MiniTest.execute()|.
------------------------------------------------------------------------------
*MiniTest.execute()*
`MiniTest.execute`({cases}, {opts})
Execute array of test cases
Overview of execution process:
- Reset `all_cases` in |MiniTest.current| with `cases` input.
- Call `reporter.start(cases)` (if present).
- Execute each case in natural array order (aligned with their integer
keys). Set `MiniTest.current.case` to currently executed case. Detailed
test case execution is described in |MiniTest-test-case|. After any state
change, call `reporter.update(case_num)` (if present), where `case_num` is an
integer key of current test case.
- Call `reporter.finish()` (if present).
Notes:
- Execution is done in asynchronous fashion with scheduling. This allows
making meaningful progress report during execution.
- This function doesn't return anything. Instead, it updates `cases` in
place with proper `exec` field. Use `all_cases` at |MiniTest.current| to
look at execution result.
Parameters ~
{cases} `(table)` Array of test cases (see |MiniTest-test-case|).
{opts} `(table|nil)` Options controlling case collection. Possible fields:
- <reporter> - table with possible callable fields `start`, `update`,
`finish`. Default: |MiniTest.gen_reporter.buffer()| in interactive
usage and |MiniTest.gen_reporter.stdout()| in headless usage.
- <stop_on_error> - whether to stop execution (see |MiniTest.stop()|)
after first error. Default: `false`.
------------------------------------------------------------------------------
*MiniTest.stop()*
`MiniTest.stop`({opts})
Stop test execution
Parameters ~
{opts} `(table|nil)` Options with fields:
- <close_all_child_neovim> - whether to close all child neovim processes
created with |MiniTest.new_child_neovim()|. Default: `true`.
------------------------------------------------------------------------------
*MiniTest.is_executing()*
`MiniTest.is_executing`()
Check if tests are being executed
Return ~
`(boolean)`
------------------------------------------------------------------------------
*MiniTest.expect*
`MiniTest.expect`
Table with expectation functions
Each function has the following behavior:
- Silently returns `true` if expectation is fulfilled.
- Throws an informative error with information helpful for debugging.
Mostly designed to be used within 'mini.test' framework.
Usage ~
>lua
local x = 1 + 1
MiniTest.expect.equality(x, 2) -- passes
MiniTest.expect.equality(x, 1) -- fails
<
------------------------------------------------------------------------------
*MiniTest.expect.equality()*
`MiniTest.expect.equality`({left}, {right})
Expect equality of two objects
Equality is tested via |vim.deep_equal()|.
Parameters ~
{left} `(any)` First object.
{right} `(any)` Second object.
------------------------------------------------------------------------------
*MiniTest.expect.no_equality()*
`MiniTest.expect.no_equality`({left}, {right})
Expect no equality of two objects
Equality is tested via |vim.deep_equal()|.
Parameters ~
{left} `(any)` First object.
{right} `(any)` Second object.
------------------------------------------------------------------------------
*MiniTest.expect.error()*
`MiniTest.expect.error`({f}, {pattern}, {...})
Expect function call to raise error
Parameters ~
{f} `(function|table)` Callable to be tested for raising error.
{pattern} `(string|nil)` Pattern which error message should match.
Use `nil` or empty string to not test for pattern matching.
{...} `(any)` Extra arguments with which `f` will be called.
------------------------------------------------------------------------------
*MiniTest.expect.no_error()*
`MiniTest.expect.no_error`({f}, {...})
Expect function call to not raise error
Parameters ~
{f} `(function|table)` Callable to be tested for raising error.
{...} `(any)` Extra arguments with which `f` will be called.
------------------------------------------------------------------------------
*MiniTest.expect.reference_screenshot()*
`MiniTest.expect.reference_screenshot`({screenshot}, {path}, {opts})
Expect equality to reference screenshot
Parameters ~
{screenshot} `(table|nil)` Array with screenshot information. Usually an output
of `child.get_screenshot()` (see |MiniTest-child-neovim.get_screenshot()|).
If `nil`, expectation passed.
{path} `(string|nil)` Path to reference screenshot. If `nil`, constructed
automatically in directory 'tests/screenshots' from current case info and
total number of times it was called inside current case. If there is no
file at `path`, it is created with content of `screenshot`.
{opts} `(table|nil)` Options:
- <force> `(boolean)` - whether to forcefully create reference screenshot.
Temporary useful during test writing. Default: `false`.
- <ignore_lines> `(table)` - array of line numbers to ignore during compare.
Default: `nil` to check all lines.
------------------------------------------------------------------------------
*MiniTest.new_expectation()*
`MiniTest.new_expectation`({subject}, {predicate}, {fail_context})
Create new expectation function
Helper for writing custom functions with behavior similar to other methods
of |MiniTest.expect|.
Parameters ~
{subject} `(string|function|table)` Subject of expectation. If callable,
called with expectation input arguments to produce string value.
{predicate} `(function|table)` Predicate callable. Called with expectation
input arguments. Output `false` or `nil` means failed expectation.
{fail_context} `(string|function|table)` Information about fail. If callable,
called with expectation input arguments to produce string value.
Return ~
`(function)` Expectation function.
Usage ~
>lua
local expect_truthy = MiniTest.new_expectation(
'truthy',
function(x) return x end,
function(x) return 'Object: ' .. vim.inspect(x) end
)
<
------------------------------------------------------------------------------
*MiniTest.gen_reporter*
`MiniTest.gen_reporter`
Table with pre-configured report generators
Each element is a function which returns reporter - table with callable
`start`, `update`, and `finish` fields.
------------------------------------------------------------------------------
*MiniTest.gen_reporter.buffer()*
`MiniTest.gen_reporter.buffer`({opts})
Generate buffer reporter
This is a default choice for interactive (not headless) usage. Opens a window
with dedicated non-terminal buffer and updates it with throttled redraws.
Opened buffer has the following helpful Normal mode mappings:
- `<Esc>` - stop test execution if executing (see |MiniTest.is_executing()|
and |MiniTest.stop()|). Close window otherwise.
- `q` - same as `<Esc>` for convenience and compatibility.
General idea:
- Group cases by concatenating first `opts.group_depth` elements of case
description (`desc` field). Groups by collected files if using default values.
- In `start()` show some stats to know how much is scheduled to be executed.
- In `update()` show symbolic overview of current group and state of current
case. Each symbol represents one case and its state:
- `?` - case didn't finish executing.
- `o` - pass.
- `O` - pass with notes.
- `x` - fail.
- `X` - fail with notes.
- In `finish()` show all fails and notes ordered by case.
Parameters ~
{opts} `(table|nil)` Table with options. Used fields:
- <group_depth> - number of first elements of case description (can be zero)
used for grouping. Higher values mean higher granularity of output.
Default: 1.
- <throttle_delay> - minimum number of milliseconds to wait between
redrawing. Reduces screen flickering but not amount of computations.
Default: 10.
- <window> - definition of window to open. Can take one of the forms:
- Callable. It is called expecting output to be target window id
(current window is used if output is `nil`). Use this to open in
"normal" window (like `function() vim.cmd('vsplit') end`).
- Table. Used as `config` argument in |nvim_open_win()|.
Default: table for centered floating window.
------------------------------------------------------------------------------
*MiniTest.gen_reporter.stdout()*
`MiniTest.gen_reporter.stdout`({opts})
Generate stdout reporter
This is a default choice for headless usage. Writes to `stdout`. Uses
coloring ANSI escape sequences to make pretty and informative output
(should work in most modern terminals and continuous integration providers).
It has same general idea as |MiniTest.gen_reporter.buffer()| with slightly
less output (it doesn't overwrite previous text) to overcome typical
terminal limitations.
Parameters ~
{opts} `(table|nil)` Table with options. Used fields:
- <group_depth> - number of first elements of case description (can be zero)
used for grouping. Higher values mean higher granularity of output.
Default: 1.
- <quit_on_finish> - whether to quit after finishing test execution.
Default: `true`.
------------------------------------------------------------------------------
*MiniTest.new_child_neovim()*
`MiniTest.new_child_neovim`()
Create child Neovim process
This creates an object designed to be a fundamental piece of 'mini.test'
methodology. It can start/stop/restart a separate (child) Neovim process
(headless, but fully functioning) together with convenience helpers to
interact with it through |RPC| messages.
For more information see |MiniTest-child-neovim|.
Return ~
MiniTest.child Object of |MiniTest-child-neovim|.
Usage ~
>lua
-- Initiate
local child = MiniTest.new_child_neovim()
child.start()
-- Use API functions
child.api.nvim_buf_set_lines(0, 0, -1, true, { 'Line inside child Neovim' })
-- Execute Lua code, Vimscript commands, etc.
child.lua('_G.n = 0')
child.cmd('au CursorMoved * lua _G.n = _G.n + 1')
child.type_keys('l')
print(child.lua_get('_G.n')) -- Should be 1
-- Use other `vim.xxx` Lua wrappers (executed inside child process)
vim.b.aaa = 'current process'
child.b.aaa = 'child process'
print(child.lua_get('vim.b.aaa')) -- Should be 'child process'
-- Always stop process after it is not needed
child.stop()
<
------------------------------------------------------------------------------
*MiniTest-child-neovim*
Child class
It offers a great set of tools to write reliable and reproducible tests by
allowing to use fresh process in any test action. Interaction with it is done
through |RPC| protocol.
Although quite flexible, at the moment it has certain limitations:
- Doesn't allow using functions or userdata for child's both inputs and
outputs. Usual solution is to move computations from current Neovim process
to child process. Use `child.lua()` and `child.lua_get()` for that.
- When writing tests, it is common to end up with "hanging" process: it
stops executing without any output. Most of the time it is because Neovim
process is "blocked", i.e. it waits for user input and won't return from
other call (like `child.api.nvim_exec_lua()`). Common causes are active
|hit-enter-prompt| (increase prompt height to a bigger value) or
Operator-pending mode (exit it). To mitigate this experience, most helpers
will throw an error if its immediate execution will lead to hanging state.
Also in case of hanging state try `child.api_notify` instead of `child.api`.
Notes:
- An important type of field is a "redirection table". It acts as a
convenience wrapper for corresponding `vim.*` table. Can be used both to
return and set values. Examples:
- `child.api.nvim_buf_line_count(0)` will execute
`vim.api.nvim_buf_line_count(0)` inside child process and return its
output to current process.
- `child.bo.filetype = 'lua'` will execute `vim.bo.filetype = 'lua'`
inside child process.
They still have same limitations listed above, so are not perfect. In
case of a doubt, use `child.lua()`.
- Almost all methods use |vim.rpcrequest()| (i.e. wait for call to finish and
then return value). See for `*_notify` variant to use |vim.rpcnotify()|.
- All fields and methods should be called with `.`, not `:`.
Class ~
{MiniTest.child}
Fields ~
{start} `(function)` Start child process. See |MiniTest-child-neovim.start()|.
{stop} `(function)` Stop current child process.
{restart} `(function)` Restart child process: stop if running and then
start a new one. Takes same arguments as `child.start()` but uses values
from most recent `start()` call as defaults.
{type_keys} `(function)` Emulate typing keys.
See |MiniTest-child-neovim.type_keys()|. Doesn't check for blocked state.
{cmd} `(function)` Execute Vimscript code from a string.
A wrapper for |nvim_exec()| without capturing output.
{cmd_capture} `(function)` Execute Vimscript code from a string and
capture output. A wrapper for |nvim_exec()| with capturing output.
{lua} `(function)` Execute Lua code. A wrapper for |nvim_exec_lua()|.
{lua_notify} `(function)` Execute Lua code without waiting for output.
{lua_get} `(function)` Execute Lua code and return result. A wrapper
for |nvim_exec_lua()| but prepends string code with `return`.
{lua_func} `(function)` Execute Lua function and return it's result.
Function will be called with all extra parameters (second one and later).
Note: usage of upvalues (data from outside function scope) is not allowed.
{is_blocked} `(function)` Check whether child process is blocked.
{is_running} `(function)` Check whether child process is currently running.
{ensure_normal_mode} `(function)` Ensure normal mode.
{get_screenshot} `(function)` Returns table with two "2d arrays" of single
characters representing what is displayed on screen and how it looks.
Has `opts` table argument for optional configuratnion.
{job} `(table|nil)` Information about current job. If `nil`, child is not running.
{api} `(table)` Redirection table for `vim.api`. Doesn't check for blocked state.
{api_notify} `(table)` Same as `api`, but uses |vim.rpcnotify()|.
{diagnostic} `(table)` Redirection table for |vim.diagnostic|.
{fn} `(table)` Redirection table for |vim.fn|.
{highlight} `(table)` Redirection table for `vim.highlight` (|lua-highlight)|.
{json} `(table)` Redirection table for `vim.json`.
{loop} `(table)` Redirection table for |vim.loop|.
{lsp} `(table)` Redirection table for `vim.lsp` (|lsp-core)|.
{mpack} `(table)` Redirection table for |vim.mpack|.
{spell} `(table)` Redirection table for |vim.spell|.
{treesitter} `(table)` Redirection table for |vim.treesitter|.
{ui} `(table)` Redirection table for `vim.ui` (|lua-ui|). Currently of no
use because it requires sending function through RPC, which is impossible
at the moment.
{g} `(table)` Redirection table for |vim.g|.
{b} `(table)` Redirection table for |vim.b|.
{w} `(table)` Redirection table for |vim.w|.
{t} `(table)` Redirection table for |vim.t|.
{v} `(table)` Redirection table for |vim.v|.
{env} `(table)` Redirection table for |vim.env|.
{o} `(table)` Redirection table for |vim.o|.
{go} `(table)` Redirection table for |vim.go|.
{bo} `(table)` Redirection table for |vim.bo|.
{wo} `(table)` Redirection table for |vim.wo|.
------------------------------------------------------------------------------
*MiniTest-child-neovim.start()*
child.start(args, opts) ~
Start child process and connect to it. Won't work if child is already running.
Parameters ~
{args} `(table)` Array with arguments for executable. Will be prepended with
the following default arguments (see |startup-options|): >lua
{ '--clean', '-n', '--listen', <some address>,
'--headless', '--cmd', 'set lines=24 columns=80' }
{opts} `(table|nil)` Options:
- <nvim_executable> - name of Neovim executable. Default: |v:progpath|.
- <connection_timeout> - stop trying to connect after this amount of
milliseconds. Default: 5000.
Usage ~
>lua
child = MiniTest.new_child_neovim()
-- Start default clean Neovim instance
child.start()
-- Start with custom 'init.lua' file
child.start({ '-u', 'scripts/minimal_init.lua' })
<
------------------------------------------------------------------------------
*MiniTest-child-neovim.type_keys()*
child.type_keys(wait, ...) ~
Basically a wrapper for |nvim_input()| applied inside child process.
Differences:
- Can wait after each group of characters.
- Raises error if typing keys resulted into error in child process (i.e. its
|v:errmsg| was updated).
- Key '<' as separate entry may not be escaped as '<LT>'.
Parameters ~
{wait} `(number|nil)` Number of milliseconds to wait after each entry. May be
omitted, in which case no waiting is done.
{...} `(string|table<number,string>)` Separate entries for |nvim_input()|,
after which `wait` will be applied. Can be either string or array of strings.
Usage ~
>lua
-- All of these type keys 'c', 'a', 'w'
child.type_keys('caw')
child.type_keys('c', 'a', 'w')
child.type_keys('c', { 'a', 'w' })
-- Waits 5 ms after `c` and after 'w'
child.type_keys(5, 'c', { 'a', 'w' })
-- Special keys can also be used
child.type_keys('i', 'Hello world', '<Esc>')
<
------------------------------------------------------------------------------
*MiniTest-child-neovim.get_screenshot()*
child.get_screenshot() ~
Compute what is displayed on (default TUI) screen and how it is displayed.
This basically calls |screenstring()| and |screenattr()| for every visible
cell (row from 1 to 'lines', column from 1 to 'columns').
Notes:
- To make output more portable and visually useful, outputs of
`screenattr()` are coded with single character symbols. Those are taken from
94 characters (ASCII codes between 33 and 126), so there will be duplicates
in case of more than 94 different ways text is displayed on screen.
Parameters ~
{opts} `(table|nil)` Options. Possieble fields:
- <redraw> `(boolean)` - whether to call |:redraw| prior to computing
screenshot. Default: `true`.
Return ~
`(table|nil)` Screenshot table with the following fields:
- <text> - "2d array" (row-column) of single characters displayed at
particular cells.
- <attr> - "2d array" (row-column) of symbols representing how text is
displayed (basically, "coded" appearance/highlighting). They should be
used only in relation to each other: same/different symbols for two
cells mean same/different visual appearance. Note: there will be false
positives if there are more than 94 different attribute values.
It also can be used with `tostring()` to convert to single string (used
for writing to reference file). It results into two visual parts
(separated by empty line), for `text` and `attr`. Each part has "ruler"
above content and line numbers for each line.
Returns `nil` if couldn't get a reasonable screenshot.
Usage ~
>lua
local screenshot = child.get_screenshot()
-- Show character displayed row=3 and column=4
print(screenshot.text[3][4])
-- Convert to string
tostring(screenshot)
<
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -0,0 +1,93 @@
*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|).
------------------------------------------------------------------------------
*MiniTrailspace.setup()*
`MiniTrailspace.setup`({config})
Module setup
Parameters ~
{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.config*
`MiniTrailspace.config`
Module config
Default values:
>lua
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,
}
<
------------------------------------------------------------------------------
*MiniTrailspace.highlight()*
`MiniTrailspace.highlight`()
Highlight trailing whitespace in current window
------------------------------------------------------------------------------
*MiniTrailspace.unhighlight()*
`MiniTrailspace.unhighlight`()
Unhighlight trailing whitespace in current window
------------------------------------------------------------------------------
*MiniTrailspace.trim()*
`MiniTrailspace.trim`()
Trim trailing whitespace
------------------------------------------------------------------------------
*MiniTrailspace.trim_last_lines()*
`MiniTrailspace.trim_last_lines`()
Trim last blank lines
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -0,0 +1,938 @@
*mini.visits* Track and reuse file system visits
*MiniVisits*
MIT License Copyright (c) 2023 Evgeni Chasnovski
==============================================================================
Features:
- Persistently track file system visits (both files and directories)
per project directory. Store visit index is human readable and editable.
- Visit index is normalized on every write to contain relevant information.
Exact details can be customized. See |MiniVisits.normalize()|.
- Built-in ability to persistently add labels to path for later use.
See |MiniVisits.add_label()| and |MiniVisits.remove_label()|.
- Exported functions to reuse visit data:
- List visited paths/labels with custom filter and sort (uses "robust
frecency" by default). Can be used as source for pickers.
See |MiniVisits.list_paths()| and |MiniVisits.list_labels()|.
See |MiniVisits.gen_filter| and |MiniVisits.gen_sort|.
- Select visited paths/labels using |vim.ui.select()|.
See |MiniVisits.select_path()| and |MiniVisits.select_labels()|.
- Iterate through visit paths in target direction ("forward", "backward",
"first", "last"). See |MiniVisits.iterate_paths()|.
- Exported functions to manually update visit index allowing persistent
track of any user information. See `*_index()` functions.
Notes:
- All data is stored _only_ in in-session Lua variable (for quick operation)
and at `config.store.path` on disk (for persistent usage).
- Most of functions affect an in-session data which gets written to disk only
before Neovim is closing or when users asks to.
- It doesn't account for paths being renamed or moved (because there is no
general way to detect that). Usually a manual intervention to the visit
index is required after the change but _before_ the next writing to disk
(usually before closing current session) because it will treat previous
path as deleted and remove it from index.
There is a |MiniVisits.rename_in_index()| helper for that.
If rename/move is done with |MiniFiles|, index is autoupdated.
Sources with more details:
- |MiniVisits-overview|
- |MiniVisits-index-specification|
- |MiniVisits-examples|
# Setup ~
This module needs a setup with `require('mini.visits').setup({})` (replace
`{}` with your `config` table). It will create global Lua table `MiniVisits`
which you can use for scripting or manually (with `:lua MiniVisits.*`).
See |MiniVisits.config| for `config` structure and default values.
You can override runtime config settings locally to buffer inside
`vim.b.minivisits_config` which should have same structure as
`MiniVisits.config`. See |mini.nvim-buffer-local-config| for more details.
# Comparisons ~
- 'nvim-telescope/telescope-frecency.nvim':
- It stores array of actual visit timestamps, while this module tracks
only total number and latest timestamp of visits. This is by design
as a different trade-off between how much data is being used/stored
and complexity of underlying "frecency" sorting.
- By default tracks a buffer only once per session, while this module
tracks on every meaningful buffer enter. This leads to a more relevant
in-session sorting.
- Implements an original frecency algorithm of Firefox's address bar,
while this module uses own "robust frecency" approach.
- Mostly designed to work with 'nvim-telescope/telescope.nvim', while
this module provides general function to list paths and select
with |vim.ui.select()|.
- Does not allow use of custom data (like labels), while this module does.
- 'ThePrimeagen/harpoon':
- Has slightly different concept than general labeling, which more
resembles adding paths to an ordered stack. This module implements
a more common labeling which does not imply order with ability to
make it automated depending on the task and/or preference.
- Implements marks as positions in a path, while this module labels paths.
- Writes data on disk after every meaning change, while this module is
more conservative and read only when Neovim closes or when asked to.
- Has support for labeling terminals, while this modules is oriented
only towards paths.
- Has dedicated UI to manage marks, while this module does not by design.
There are functions for adding and removing label from the path.
- Does not provide functionality to track and reuse any visited path,
while this module does.
# Disabling ~
To disable automated tracking, set `vim.g.minivisits_disable` (globally) or
`vim.b.minivisits_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.
------------------------------------------------------------------------------
*MiniVisits-overview*
# Tracking visits ~
File system visits (both directory and files) tracking is done in two steps:
- On every dedicated event (`config.track.event`, |BufEnter| by default) timer
is (re)started to actually register visit after certain amount of time
(`config.track.delay` milliseconds, 1000 by default). It is not registered
immediately to allow navigation to target buffer in several steps
(for example, with series of |:bnext| / |:bprevious|).
- When delay time passes without any dedicated events being triggered
(meaning user is "settled" on certain buffer), |MiniVisits.register_visit()|
is called if all of the following conditions are met:
- Module is not disabled (see "Disabling" section in |MiniVisits|).
- Buffer is normal with non-empty name (used as visit path).
- Visit path does not equal to the latest tracked one. This is to allow
temporary enter of non-normal buffers (like help, terminal, etc.)
without artificial increase of visit count.
Visit is autoregistered for |current-directory| and leads to increase of count
and latest time of visit. See |MiniVisits-index-specification| for more details.
Notes:
- All data is stored _only_ in in-session Lua variable (for quick operation)
and at `config.store.path` on disk (for persistent usage). It is automatically
written to disk before every Neovim exit (if `config.store.autowrite` is set).
- Tracking can be disabled by supplying empty string as `track.event`.
Then it is up to the user to properly call |MiniVisits.register_visit()|.
# Reusing visits ~
Visit data can be reused in at least these ways:
- Get a list of visited paths (see |MiniVisits.list_paths()|) and use it
to visualize/pick/navigate visit history.
- Select one of the visited paths to open it (see |MiniVisits.select_path()|).
- Move along visit history (see |MiniVisits.iterate_paths()|).
- Utilize labels. Any visit can be added one or more labels (like "core",
"tmp", etc.). They are bound to the visit (path registered for certain
directory) and are stored persistently.
Labels can be used to manually create groups of files and/or directories
that have particular interest to the user.
There is no one right way to use them, though. See |MiniVisits-examples|
for some inspiration.
- Utilizing custom data. Visit index can be manipulated manually using
`_index()` set of functions. All "storable" (i.e. not functions or
metatables) user data inside index is then stored on disk, so it can be
used to create any kind of workflow user wants.
See |MiniVisits-examples| for some actual configuration and workflow examples.
------------------------------------------------------------------------------
*MiniVisits-index-specification*
# Structure ~
Visit index is a table containing actual data in two level deep nested tables.
First level keys are paths of project directory (a.k.a "cwd") for which
visits are registered.
Second level keys are actual visit paths. Their values are tables with visit
data which should follow these requirements:
- Field <count> should be present and be a number. It represents the number
of times this path was visited under particular cwd.
- Field <latest> should be present and be a number. It represents the time
of latest path visit under particular cwd.
By default computed with |os.time()| (up to a second).
- Field <labels> might not be present. If present, it should be a table
with string labels as keys and `true` as values. It represents labels of
the path under particular cwd.
Notes:
- All paths are absolute.
- Visit path should not necessarily be a part of corresponding cwd.
- Both `count` and `latest` can be any number: whole, fractional, negative, etc.
Example of an index data: >lua
{
['/home/user/project_1'] = {
['home/user/project_1/file'] = { count = 3, latest = 1699796000 },
['home/user/project_1/subdir'] = {
count = 10, latest = 1699797000, labels = { core = true },
},
},
['/home/user/project_2'] = {
['home/user/project_1/file'] = {
count = 0, latest = 0, labels = { other = true },
},
['home/user/project_2/README'] = { count = 1, latest = 1699798000 },
},
}
<
# Storage ~
When stored on disk, visit index is a file containing Lua code returning
visit index table. It can be edited by hand as long as it contains a valid
Lua code (to be executed with |dofile()|).
Notes:
- Storage is implemented in such a way that it doesn't really support more
than one parallel Neovim processes. Meaning that if there are two or more
simultaneous Neovim processes with same visit index storage path, the last
one writing to it will preserve its visit history while others - won't.
# Normalization ~
To ensure that visit index contains mostly relevant data, it gets normalized:
automatically inside |MiniVisits.write_index()| or via |MiniVisits.normalize()|.
What normalization actually does can be configured in `config.store.normalize`.
See |MiniVisits.gen_normalize.default()| for default normalization approach.
------------------------------------------------------------------------------
*MiniVisits-examples*
# Workflow examples ~
This module provides a flexible framework for working with file system visits.
Exact choice of how to organize workflow is left to the user.
Here are some examples for inspiration which can be combined together.
## Use different sorting ~
Default sorting in |MiniVisits.gen_sort.default()| allows flexible adjustment
of which feature to prefer more: recency or frequency. Here is an example of
how to make set of keymaps for three types of sorting combined with two types
of scopes (all visits and only for current cwd): >lua
local make_select_path = function(select_global, recency_weight)
local visits = require('mini.visits')
local sort = visits.gen_sort.default({ recency_weight = recency_weight })
local select_opts = { sort = sort }
return function()
local cwd = select_global and '' or vim.fn.getcwd()
visits.select_path(cwd, select_opts)
end
end
local map = function(lhs, desc, ...)
vim.keymap.set('n', lhs, make_select_path(...), { desc = desc })
end
-- Adjust LHS and description to your liking
map('<Leader>vr', 'Select recent (all)', true, 1)
map('<Leader>vR', 'Select recent (cwd)', false, 1)
map('<Leader>vy', 'Select frecent (all)', true, 0.5)
map('<Leader>vY', 'Select frecent (cwd)', false, 0.5)
map('<Leader>vf', 'Select frequent (all)', true, 0)
map('<Leader>vF', 'Select frequent (cwd)', false, 0)
<
Note: If you have |MiniPick|, consider using |MiniExtra.pickers.visit_paths()|.
## Use manual labels ~
Labels is a powerful tool to create groups of associated paths.
Usual workflow consists of:
- Add label with |MiniVisits.add_label()| (prompts for actual label).
- Remove label with |MiniVisits.remove_label()| (prompts for actual label).
- When need to use labeled groups, call |MiniVisits.select_label()| which
will then call |MiniVisits.select_path()| to select path among those
having selected label.
Note: If you have |MiniPick|, consider using |MiniExtra.pickers.visit_labels()|.
To make this workflow smoother, here is an example of keymaps: >lua
local map_vis = function(keys, call, desc)
local rhs = '<Cmd>lua MiniVisits.' .. call .. '<CR>'
vim.keymap.set('n', '<Leader>' .. keys, rhs, { desc = desc })
end
map_vis('vv', 'add_label()', 'Add label')
map_vis('vV', 'remove_label()', 'Remove label')
map_vis('vl', 'select_label("", "")', 'Select label (all)')
map_vis('vL', 'select_label()', 'Select label (cwd)')
<
## Use fixed labels ~
During work on every project there is usually a handful of files where core
activity is concentrated. This can be made easier by creating mappings
which add/remove special fixed label (for example, "core") and select paths
with that label for both all and current cwd. Example: >lua
-- Create and select
local map_vis = function(keys, call, desc)
local rhs = '<Cmd>lua MiniVisits.' .. call .. '<CR>'
vim.keymap.set('n', '<Leader>' .. keys, rhs, { desc = desc })
end
map_vis('vv', 'add_label("core")', 'Add to core')
map_vis('vV', 'remove_label("core")', 'Remove from core')
map_vis('vc', 'select_path("", { filter = "core" })', 'Select core (all)')
map_vis('vC', 'select_path(nil, { filter = "core" })', 'Select core (cwd)')
-- Iterate based on recency
local map_iterate_core = function(lhs, direction, desc)
local opts = { filter = 'core', sort = sort_latest, wrap = true }
local rhs = function()
MiniVisits.iterate_paths(direction, vim.fn.getcwd(), opts)
end
vim.keymap.set('n', lhs, rhs, { desc = desc })
end
map_iterate_core('[{', 'last', 'Core label (earliest)')
map_iterate_core('[[', 'forward', 'Core label (earlier)')
map_iterate_core(']]', 'backward', 'Core label (later)')
map_iterate_core(']}', 'first', 'Core label (latest)')
<
## Use automated labels ~
When using version control system (such as Git), usually there is already
an identifier that groups files you are working with - branch name.
Here is an example of keymaps to add/remove label equal to branch name: >lua
local map_branch = function(keys, action, desc)
local rhs = function()
local branch = vim.fn.system('git rev-parse --abbrev-ref HEAD')
if vim.v.shell_error ~= 0 then return nil end
branch = vim.trim(branch)
require('mini.visits')[action](branch)
end
vim.keymap.set('n', '<Leader>' .. keys, rhs, { desc = desc })
end
map_branch('vb', 'add_label', 'Add branch label')
map_branch('vB', 'remove_label', 'Remove branch label')
<
------------------------------------------------------------------------------
*MiniVisits.setup()*
`MiniVisits.setup`({config})
Module setup
Parameters ~
{config} `(table|nil)` Module config table. See |MiniVisits.config|.
Usage ~
>lua
require('mini.visits').setup() -- use default config
-- OR
require('mini.visits').setup({}) -- replace {} with your config table
<
------------------------------------------------------------------------------
*MiniVisits.config*
`MiniVisits.config`
Module config
Default values:
>lua
MiniVisits.config = {
-- How visit index is converted to list of paths
list = {
-- Predicate for which paths to include (all by default)
filter = nil,
-- Sort paths based on the visit data (robust frecency by default)
sort = nil,
},
-- Whether to disable showing non-error feedback
silent = false,
-- How visit index is stored
store = {
-- Whether to write all visits before Neovim is closed
autowrite = true,
-- Function to ensure that written index is relevant
normalize = nil,
-- Path to store visit index
path = vim.fn.stdpath('data') .. '/mini-visits-index',
},
-- How visit tracking is done
track = {
-- Start visit register timer at this event
-- Supply empty string (`''`) to not do this automatically
event = 'BufEnter',
-- Debounce delay after event to register a visit
delay = 1000,
},
}
<
*MiniVisits.config.list*
# List ~
`config.list` defines how visit index is converted to a path list by default.
`list.filter` is a callable which should take a path data and return `true` if
this path should be present in the list.
Default: output of |MiniVisits.gen_filter.default()|.
Path data is a table with at least these fields:
- <path> `(string)` - absolute path of visit.
- <count> `(number)` - number of visits.
- <latest> `(number)` - timestamp of latest visit.
- <labels> `(table|nil)` - table of labels (has string keys with `true` values).
Notes:
- Both `count` and `latest` (in theory) can be any number. But built-in tracking
results into positive integer `count` and `latest` coming from |os.time()|.
- There can be other entries if they are set by user as index entry.
`list.sort` is a callable which should take an array of path data and return
a sorted array of path data (or at least tables each containing <path> field).
Default: output of |MiniVisits.get_sort.default()|.
Single path data entry is a table with a same structure as for `list.filter`.
Note, that `list.sort` can be used both to filter, sort, or even return paths
unrelated to the input.
# Silent ~
`config.silent` is a boolean controlling whether to show non-error feedback
(like adding/removing labels, etc.). Default: `false`.
# Store ~
`config.store` defines how visit index is stored on disk to enable persistent
data across several sessions.
`store.autowrite` is a boolean controlling whether to write visit data to
disk on |VimLeavePre| event. Default: `true`.
`store.normalize` is a callable which should take visit index
(see |MiniVisits-index-specification|) as input and return "normalized" visit
index as output. This is used to ensure that visit index is up to date and
contains only relevant data. For example, it controls how old and
irrelevant visits are "forgotten", and more.
Default: output of |MiniVisits.gen_normalize.default()|.
`store.path` is a path to which visit index is written. See "Storage" section
of |MiniVisits-index-specification| for more details.
Note: set to empty string to disable any writing with not explicitly set
path (including the one on |VimLeavePre|).
Default: "mini-visits-index" file inside |$XDG_DATA_HOME|.
# Track ~
`config.track` defines how visits are tracked (index entry is autoupdated).
See "Tracking visits" section in |MiniVisits-overview| for more details.
`track.event` is a proper Neovim |{event}| on which track get triggered.
Note: set to empty string to disable automated tracking.
Default: |BufEnter|.
`track.delay` is a delay in milliseconds after event is triggered and visit
is autoregistered.
Default: 1000 (to allow navigation between buffers without tracking
intermediate ones).
------------------------------------------------------------------------------
*MiniVisits.register_visit()*
`MiniVisits.register_visit`({path}, {cwd})
Register visit
Steps:
- Ensure that there is an entry for path-cwd pair.
- Add 1 to visit `count`.
- Set `latest` visit time to equal current time.
Parameters ~
{path} `(string|nil)` Visit path. Default: path of current buffer.
{cwd} `(string|nil)` Visit cwd (project directory). Default: |current-directory|.
------------------------------------------------------------------------------
*MiniVisits.add_path()*
`MiniVisits.add_path`({path}, {cwd})
Add path to index
Ensures that there is a (one or more) entry for path-cwd pair. If entry is
already present, does nothing. If not - creates it with both `count` and
`latest` set to 0.
Parameters ~
{path} `(string|nil)` Visit path. Can be empty string to mean "all visited
paths for `cwd`". Default: path of current buffer.
{cwd} `(string|nil)` Visit cwd (project directory). Can be empty string to mean
"all visited cwd". Default: |current-directory|.
------------------------------------------------------------------------------
*MiniVisits.add_label()*
`MiniVisits.add_label`({label}, {path}, {cwd})
Add label to path
Steps:
- Ensure that there is an entry for path-cwd pair.
- Add label to the entry.
Parameters ~
{label} `(string|nil)` Label string. Default: `nil` to ask from user.
{path} `(string|nil)` Visit path. Can be empty string to mean "all visited
paths for `cwd`". Default: path of current buffer.
{cwd} `(string|nil)` Visit cwd (project directory). Can be empty string to mean
"all visited cwd". Default: |current-directory|.
------------------------------------------------------------------------------
*MiniVisits.remove_path()*
`MiniVisits.remove_path`({path}, {cwd})
Remove path
Deletes a (one or more) entry for path-cwd pair from an index. If entry is
already absent, does nothing.
Notes:
- Affects only in-session Lua variable. Call |MiniVisits.write_index()| to
make it persistent.
Parameters ~
{path} `(string|nil)` Visit path. Can be empty string to mean "all visited
paths for `cwd`". Default: path of current buffer.
{cwd} `(string|nil)` Visit cwd (project directory). Can be empty string to mean
"all visited cwd". Default: |current-directory|.
------------------------------------------------------------------------------
*MiniVisits.remove_label()*
`MiniVisits.remove_label`({label}, {path}, {cwd})
Remove label from path
Steps:
- Remove label from (one or more) index entry.
- If it was last label in an entry, remove `labels` key.
Parameters ~
{label} `(string|nil)` Label string. Default: `nil` to ask from user.
{path} `(string|nil)` Visit path. Can be empty string to mean "all visited
paths for `cwd`". Default: path of current buffer.
{cwd} `(string|nil)` Visit cwd (project directory). Can be empty string to mean
"all visited cwd". Default: |current-directory|.
------------------------------------------------------------------------------
*MiniVisits.list_paths()*
`MiniVisits.list_paths`({cwd}, {opts})
List visit paths
Convert visit index for certain cwd into an ordered list of visited paths.
Supports custom filtering and sorting.
Examples: >lua
-- Get paths sorted from most to least recent
local sort_recent = MiniVisits.gen_sort.default({ recency_weight = 1 })
MiniVisits.list_paths(nil, { sort = sort_recent })
-- Get paths from all cwd sorted from most to least frequent
local sort_frequent = MiniVisits.gen_sort.default({ recency_weight = 0 })
MiniVisits.list_paths('', { sort = sort_frequent })
-- Get paths not including hidden
local is_not_hidden = function(path_data)
return not vim.startswith(vim.fn.fnamemodify(path_data.path, ':t'), '.')
end
MiniVisits.list_paths(nil, { filter = is_not_hidden })
<
Parameters ~
{cwd} `(string|nil)` Visit cwd (project directory). Can be empty string to mean
"all visited cwd". Default: |current-directory|.
{opts} `(table|nil)` Options. Possible fields:
- <filter> `(function)` - predicate to filter paths. For more information
about how it is used, see |MiniVisits.config.list|.
Default: value of `config.list.filter` with |MiniVisits.gen_filter.default()|
as its default.
- <sort> `(function)` - path data sorter. For more information about how
it is used, see |MiniVisits.config.list|.
Default: value of `config.list.sort` or |MiniVisits.gen_filter.sort()|
as its default.
Return ~
`(table)` Array of visited paths.
------------------------------------------------------------------------------
*MiniVisits.list_labels()*
`MiniVisits.list_labels`({path}, {cwd}, {opts})
List visit labels
Convert visit index for certain path-cwd pair into an ordered list of labels.
Supports custom filtering for paths. Result is ordered from most to least
frequent label.
Examples: >lua
-- Get labels for current path-cwd pair
MiniVisits.list_labels()
-- Get labels for current path across all cwd
MiniVisits.list_labels(nil, '')
-- Get all available labels excluding ones from hidden files
local is_not_hidden = function(path_data)
return not vim.startswith(vim.fn.fnamemodify(path_data.path, ':t'), '.')
end
MiniVisits.list_labels('', '', { filter = is_not_hidden })
<
Parameters ~
{path} `(string|nil)` Visit path. Can be empty string to mean "all visited
paths for `cwd`". Default: path of current buffer.
{cwd} `(string|nil)` Visit cwd (project directory). Can be empty string to mean
"all visited cwd". Default: |current-directory|.
{opts} `(table|nil)` Options. Possible fields:
- <filter> `(function)` - predicate to filter paths. For more information
about how it is used, see |MiniVisits.config.list|.
Default: value of `config.list.filter` with |MiniVisits.gen_filter.default()|
as its default.
- <sort> `(function)` - path data sorter. For more information about how
it is used, see |MiniVisits.config.list|.
Default: value of `config.list.sort` or |MiniVisits.gen_filter.sort()|
as its default.
Return ~
`(table)` Array of available labels.
------------------------------------------------------------------------------
*MiniVisits.select_path()*
`MiniVisits.select_path`({cwd}, {opts})
Select visit path
Uses |vim.ui.select()| with an output of |MiniVisits.list_paths()| and
calls |:edit| on the chosen item.
Note: if you have |MiniPick|, consider using |MiniExtra.pickers.visits()|.
Examples:
- Select from all visited paths: `MiniVisits.select_path('')`
- Select from paths under current directory sorted from most to least recent: >lua
local sort_recent = MiniVisits.gen_sort.default({ recency_weight = 1 })
MiniVisits.select_path(nil, { sort = sort_recent })
<
Parameters ~
{cwd} `(string|nil)` Forwarded to |MiniVisits.list_paths()|.
{opts} `(table|nil)` Forwarded to |MiniVisits.list_paths()|.
------------------------------------------------------------------------------
*MiniVisits.select_label()*
`MiniVisits.select_label`({path}, {cwd}, {opts})
Select visit label
Uses |vim.ui.select()| with an output of |MiniVisits.list_labels()| and
calls |MiniVisits.select_path()| to get target paths with selected label.
Note: if you have |MiniPick|, consider using |MiniExtra.pickers.visit_labels()|.
Examples:
- Select from labels of current path: `MiniVisits.select_label()`
- Select from all visited labels: `MiniVisits.select_label('', '')`
- Select from current project labels and sort paths (after choosing) from most
to least recent: >lua
local sort_recent = MiniVisits.gen_sort.default({ recency_weight = 1 })
MiniVisits.select_label('', nil, { sort = sort_recent })
<
Parameters ~
{path} `(string|nil)` Forwarded to |MiniVisits.list_labels()|.
{cwd} `(string|nil)` Forwarded to |MiniVisits.list_labels()|.
{opts} `(table|nil)` Forwarded to both |MiniVisits.list_labels()|
and |MiniVisits.select_paths()| (after choosing a label).
------------------------------------------------------------------------------
*MiniVisits.iterate_paths()*
`MiniVisits.iterate_paths`({direction}, {cwd}, {opts})
Iterate visit paths
Steps:
- Compute a sorted array of target paths using |MiniVisits.list_paths()|.
- Identify the current index inside the array based on path of current buffer.
- Iterate through the array certain amount of times in a dedicated direction:
- For "first" direction - forward starting from index 0 (so that single
first iteration leads to first path).
- For "backward" direction - backward starting from current index.
- For "forward" direction - forward starting from current index.
- For "last" direction - backward starting from index after the last one
(so that single first iteration leads to the last path).
Notes:
- Mostly designed to be used as a mapping. See `MiniVisits-examples`.
- If path from current buffer is not in the output of `MiniVisits.list_paths()`,
starting index is inferred such that first iteration lands on first item
(if iterating forward) or last item (if iterating backward).
- Navigation with this function is not tracked (see |MiniVisits-overview|).
This is done to allow consecutive application without affecting
underlying list of paths.
Examples assuming underlying array of files `{ "file1", "file2", "file3" }`:
- `MiniVisits("first")` results into focusing on "file1".
- `MiniVisits("backward", { n_times = 2 })` from "file3" results into "file1".
- `MiniVisits("forward", { n_times = 10 })` from "file1" results into "file3".
- `MiniVisits("last", { n_times = 4, wrap = true })` results into "file3".
Parameters ~
{direction} `(string)` One of "first", "backward", "forward", "last".
{cwd} `(string|nil)` Forwarded to |MiniVisits.list_paths()|.
{opts} `(table|nil)` Options. Possible fields:
- <filter> `(function)` - forwarded to |MiniVisits.list_paths()|.
- <sort> `(function)` - forwarded to |MiniVisits.list_paths()|.
- <n_times> `(number)` - number of steps to go in certain direction.
Default: |v:count1|.
- <wrap> `(boolean)` - whether to wrap around list edges. Default: `false`.
------------------------------------------------------------------------------
*MiniVisits.get_index()*
`MiniVisits.get_index`()
Get active visit index
Return ~
`(table)` Copy of currently active visit index table.
------------------------------------------------------------------------------
*MiniVisits.set_index()*
`MiniVisits.set_index`({index})
Set active visit index
Parameters ~
{index} `(table)` Visit index table.
------------------------------------------------------------------------------
*MiniVisits.reset_index()*
`MiniVisits.reset_index`()
Reset active visit index
Set currently active visit index to the output of |MiniVisits.read_index()|.
Does nothing if reading the index failed.
------------------------------------------------------------------------------
*MiniVisits.normalize_index()*
`MiniVisits.normalize_index`({index})
Normalize visit index
Applies `config.store.normalize` (|MiniVisits.gen_normalize.default()| by default)
to the input index object and returns the output (if it fits in the definition
of index object; see |MiniVisits-index-specification|).
Parameters ~
{index} `(table|nil)` Index object. Default: copy of the current index.
Return ~
`(table)` Normalized index object.
------------------------------------------------------------------------------
*MiniVisits.read_index()*
`MiniVisits.read_index`({store_path})
Read visit index from disk
Parameters ~
{store_path} `(string|nil)` Path on the disk containing visit index data.
Default: `config.store.path`.
Notes:
- Can return `nil` if path is empty string or file is not readable.
- File is sourced with |dofile()| as a regular Lua file.
Return ~
`(table|nil)` Output of the file source.
------------------------------------------------------------------------------
*MiniVisits.write_index()*
`MiniVisits.write_index`({store_path}, {index})
Write visit index to disk
Steps:
- Normalize index with |MiniVisits.normalize_index()|.
- Ensure path is valid (all parent directories are created, etc.).
- Write index object to the path so that it is readable
with |MiniVisits.read_index()|.
Parameters ~
{store_path} `(string|nil)` Path on the disk where to write visit index data.
Default: `config.store.path`. Note: if empty string, nothing is written.
{index} `(table|nil)` Index object to write to disk.
Default: current session index.
------------------------------------------------------------------------------
*MiniVisits.rename_in_index()*
`MiniVisits.rename_in_index`({path_from}, {path_to}, {index})
Rename path in index
A helper to react for a path rename/move in order to preserve its visit data.
It works both for file and directory paths.
Notes:
- It does not update current index, but returns a modified index object.
Use |MiniVisits.set_index()| to make it current.
- Use only full paths.
- Do not append `/` to directory paths. Use same format as for files.
Assuming `path_from` and `path_to` are variables containing full paths
before and after rename/move, here is an example to update current index: >lua
local new_index = MiniVisits.rename_in_index(path_from, path_to)
MiniVisits.set_index(new_index)
<
Parameters ~
{path_from} `(string)` Full path to be renamed.
{path_to} `(string)` Full path to be replaced with.
{index} `(table|nil)` Index object inside which to perform renaming.
Default: current session index.
Return ~
`(table)` Index object with renamed path.
------------------------------------------------------------------------------
*MiniVisits.gen_filter*
`MiniVisits.gen_filter`
Generate filter function
This is a table with function elements. Call to actually get specification.
------------------------------------------------------------------------------
*MiniVisits.gen_filter.default()*
`MiniVisits.gen_filter.default`()
Default filter
Always returns `true` resulting in no actual filter.
Return ~
`(function)` Visit filter function. See |MiniVisits.config.list| for more details.
------------------------------------------------------------------------------
*MiniVisits.gen_filter.this_session()*
`MiniVisits.gen_filter.this_session`()
Filter visits from current session
Return ~
`(function)` Visit filter function. See |MiniVisits.config.list| for more details.
------------------------------------------------------------------------------
*MiniVisits.gen_sort*
`MiniVisits.gen_sort`
Generate sort function
This is a table with function elements. Call to actually get specification.
------------------------------------------------------------------------------
*MiniVisits.gen_sort.default()*
`MiniVisits.gen_sort.default`({opts})
Default sort
Sort paths using "robust frecency" approach. It relies on the rank operation:
based on certain reference number for every item, assign it a number
between 1 (best) and number of items (worst). Ties are dealt with "average
rank" approach: each element with a same reference number is assigned
an average rank among such elements. This way total rank sum depends only
on number of paths.
Here is an algorithm outline:
- Rank paths based on frequency (`count` value): from most to least frequent.
- Rank paths based on recency (`latest` value): from most to least recent.
- Combine ranks from previous steps with weights:
`score = (1 - w) * rank_frequency + w * rank_recency`, where `w` is
"recency weight". The smaller this weight the less recency affects outcome.
Examples:
- Default recency weight 0.5 results into "robust frecency" sorting: it
combines both frequency and recency.
This is called a "robust frecency" because actual values don't have direct
effect on the outcome, only ordering matters. For example, if there is
a very frequent file with `count = 100` while all others have `count = 5`,
it will not massively dominate the outcome as long as it is not very recent.
- Having recency weight 1 results into "from most to least recent" sorting.
- Having recency weight 0 results into "from most to least frequent" sorting.
Parameters ~
{opts} `(table|nil)` Option. Possible fields:
- <recency_weight> `(number)` - a number between 0 and 1 for recency weight.
Default: 0.5.
Return ~
`(function)` Visit sort function. See |MiniVisits.config.list| for more details.
------------------------------------------------------------------------------
*MiniVisits.gen_sort.z()*
`MiniVisits.gen_sort.z`()
Z sort
Sort as in https://github.com/rupa/z.
Return ~
`(function)` Visit sort function. See |MiniVisits.config.list| for more details.
------------------------------------------------------------------------------
*MiniVisits.gen_normalize*
`MiniVisits.gen_normalize`
Generate normalize function
This is a table with function elements. Call to actually get specification.
------------------------------------------------------------------------------
*MiniVisits.gen_normalize.default()*
`MiniVisits.gen_normalize.default`({opts})
Generate default normalize function
Steps:
- Prune visits, i.e. remove outdated visits:
- If `count` number of visits is below prune threshold, remove that visit
entry from particular cwd (it can still be present in others).
- If either first (cwd) or second (path) level key doesn't represent an
actual path on disk, remove the whole associated value.
- NOTE: if visit has any label, it is not automatically pruned.
- Decay visits, i.e. possibly make visits more outdated. This is an important
part to the whole usability: together with pruning it results into automated
removing of paths which were visited long ago and are not relevant.
Decay is done per cwd if its total `count` values sum exceeds decay threshold.
It is performed through multiplying each `count` by same coefficient so that
the new total sum of `count` is equal to some smaller target value.
Note: only two decimal places are preserved, so the sum might not be exact.
- Prune once more to ensure that there are no outdated paths after decay.
Parameters ~
{opts} `(table|nil)` Options. Possible fields:
- <decay_threshold> `(number)` - decay threshold. Default: 1000.
- <decay_target> `(number)` - decay target. Default: 800.
- <prune_threshold> `(number)` - prune threshold. Default: 0.5.
- <prune_paths> `(boolean)` - whether to prune outdated paths. Default: `true`.
Return ~
`(function)` Visit index normalize function. See "Store" in |MiniVisits.config|.
vim:tw=78:ts=8:noet:ft=help:norl:

View 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.
------------------------------------------------------------------------------
*mini.nvim-disabling-recipes*
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 })
<
------------------------------------------------------------------------------
*mini.nvim-buffer-local-config*
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|.
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -0,0 +1,740 @@
:Colorscheme mini-colors.txt /*:Colorscheme*
:DepsAdd mini-deps.txt /*:DepsAdd*
:DepsClean mini-deps.txt /*:DepsClean*
:DepsShowLog mini-deps.txt /*:DepsShowLog*
:DepsSnapLoad mini-deps.txt /*:DepsSnapLoad*
:DepsSnapSave mini-deps.txt /*:DepsSnapSave*
:DepsUpdate mini-deps.txt /*:DepsUpdate*
:DepsUpdateOffline mini-deps.txt /*:DepsUpdateOffline*
:Git mini-git.txt /*:Git*
:Pick mini-pick.txt /*:Pick*
MiniAi mini-ai.txt /*MiniAi*
MiniAi-algorithm mini-ai.txt /*MiniAi-algorithm*
MiniAi-glossary mini-ai.txt /*MiniAi-glossary*
MiniAi-textobject-builtin mini-ai.txt /*MiniAi-textobject-builtin*
MiniAi-textobject-specification mini-ai.txt /*MiniAi-textobject-specification*
MiniAi.config mini-ai.txt /*MiniAi.config*
MiniAi.find_textobject() mini-ai.txt /*MiniAi.find_textobject()*
MiniAi.gen_spec mini-ai.txt /*MiniAi.gen_spec*
MiniAi.gen_spec.argument() mini-ai.txt /*MiniAi.gen_spec.argument()*
MiniAi.gen_spec.function_call() mini-ai.txt /*MiniAi.gen_spec.function_call()*
MiniAi.gen_spec.pair() mini-ai.txt /*MiniAi.gen_spec.pair()*
MiniAi.gen_spec.treesitter() mini-ai.txt /*MiniAi.gen_spec.treesitter()*
MiniAi.move_cursor() mini-ai.txt /*MiniAi.move_cursor()*
MiniAi.select_textobject() mini-ai.txt /*MiniAi.select_textobject()*
MiniAi.setup() mini-ai.txt /*MiniAi.setup()*
MiniAlign mini-align.txt /*MiniAlign*
MiniAlign-algorithm mini-align.txt /*MiniAlign-algorithm*
MiniAlign-examples mini-align.txt /*MiniAlign-examples*
MiniAlign-glossary mini-align.txt /*MiniAlign-glossary*
MiniAlign-modifiers-builtin mini-align.txt /*MiniAlign-modifiers-builtin*
MiniAlign.align_strings() mini-align.txt /*MiniAlign.align_strings()*
MiniAlign.align_user() mini-align.txt /*MiniAlign.align_user()*
MiniAlign.as_parts() mini-align.txt /*MiniAlign.as_parts()*
MiniAlign.config mini-align.txt /*MiniAlign.config*
MiniAlign.gen_step mini-align.txt /*MiniAlign.gen_step*
MiniAlign.gen_step.default_justify() mini-align.txt /*MiniAlign.gen_step.default_justify()*
MiniAlign.gen_step.default_merge() mini-align.txt /*MiniAlign.gen_step.default_merge()*
MiniAlign.gen_step.default_split() mini-align.txt /*MiniAlign.gen_step.default_split()*
MiniAlign.gen_step.filter() mini-align.txt /*MiniAlign.gen_step.filter()*
MiniAlign.gen_step.ignore_split() mini-align.txt /*MiniAlign.gen_step.ignore_split()*
MiniAlign.gen_step.pair() mini-align.txt /*MiniAlign.gen_step.pair()*
MiniAlign.gen_step.trim() mini-align.txt /*MiniAlign.gen_step.trim()*
MiniAlign.new_step() mini-align.txt /*MiniAlign.new_step()*
MiniAlign.setup() mini-align.txt /*MiniAlign.setup()*
MiniAnimate mini-animate.txt /*MiniAnimate*
MiniAnimate-done-event mini-animate.txt /*MiniAnimate-done-event*
MiniAnimate-timing mini-animate.txt /*MiniAnimate-timing*
MiniAnimate.animate() mini-animate.txt /*MiniAnimate.animate()*
MiniAnimate.config mini-animate.txt /*MiniAnimate.config*
MiniAnimate.config.close mini-animate.txt /*MiniAnimate.config.close*
MiniAnimate.config.cursor mini-animate.txt /*MiniAnimate.config.cursor*
MiniAnimate.config.open mini-animate.txt /*MiniAnimate.config.open*
MiniAnimate.config.resize mini-animate.txt /*MiniAnimate.config.resize*
MiniAnimate.config.scroll mini-animate.txt /*MiniAnimate.config.scroll*
MiniAnimate.execute_after() mini-animate.txt /*MiniAnimate.execute_after()*
MiniAnimate.gen_path mini-animate.txt /*MiniAnimate.gen_path*
MiniAnimate.gen_path.angle() mini-animate.txt /*MiniAnimate.gen_path.angle()*
MiniAnimate.gen_path.line() mini-animate.txt /*MiniAnimate.gen_path.line()*
MiniAnimate.gen_path.spiral() mini-animate.txt /*MiniAnimate.gen_path.spiral()*
MiniAnimate.gen_path.walls() mini-animate.txt /*MiniAnimate.gen_path.walls()*
MiniAnimate.gen_subresize mini-animate.txt /*MiniAnimate.gen_subresize*
MiniAnimate.gen_subresize.equal() mini-animate.txt /*MiniAnimate.gen_subresize.equal()*
MiniAnimate.gen_subscroll mini-animate.txt /*MiniAnimate.gen_subscroll*
MiniAnimate.gen_subscroll.equal() mini-animate.txt /*MiniAnimate.gen_subscroll.equal()*
MiniAnimate.gen_timing mini-animate.txt /*MiniAnimate.gen_timing*
MiniAnimate.gen_timing.cubic() mini-animate.txt /*MiniAnimate.gen_timing.cubic()*
MiniAnimate.gen_timing.exponential() mini-animate.txt /*MiniAnimate.gen_timing.exponential()*
MiniAnimate.gen_timing.linear() mini-animate.txt /*MiniAnimate.gen_timing.linear()*
MiniAnimate.gen_timing.none() mini-animate.txt /*MiniAnimate.gen_timing.none()*
MiniAnimate.gen_timing.quadratic() mini-animate.txt /*MiniAnimate.gen_timing.quadratic()*
MiniAnimate.gen_timing.quartic() mini-animate.txt /*MiniAnimate.gen_timing.quartic()*
MiniAnimate.gen_winblend mini-animate.txt /*MiniAnimate.gen_winblend*
MiniAnimate.gen_winblend.linear() mini-animate.txt /*MiniAnimate.gen_winblend.linear()*
MiniAnimate.gen_winconfig mini-animate.txt /*MiniAnimate.gen_winconfig*
MiniAnimate.gen_winconfig.center() mini-animate.txt /*MiniAnimate.gen_winconfig.center()*
MiniAnimate.gen_winconfig.static() mini-animate.txt /*MiniAnimate.gen_winconfig.static()*
MiniAnimate.gen_winconfig.wipe() mini-animate.txt /*MiniAnimate.gen_winconfig.wipe()*
MiniAnimate.is_active() mini-animate.txt /*MiniAnimate.is_active()*
MiniAnimate.setup() mini-animate.txt /*MiniAnimate.setup()*
MiniBase16 mini-base16.txt /*MiniBase16*
MiniBase16.config mini-base16.txt /*MiniBase16.config*
MiniBase16.mini_palette() mini-base16.txt /*MiniBase16.mini_palette()*
MiniBase16.rgb_palette_to_cterm_palette() mini-base16.txt /*MiniBase16.rgb_palette_to_cterm_palette()*
MiniBase16.setup() mini-base16.txt /*MiniBase16.setup()*
MiniBasics mini-basics.txt /*MiniBasics*
MiniBasics.config mini-basics.txt /*MiniBasics.config*
MiniBasics.config.autocommands mini-basics.txt /*MiniBasics.config.autocommands*
MiniBasics.config.mappings mini-basics.txt /*MiniBasics.config.mappings*
MiniBasics.config.options mini-basics.txt /*MiniBasics.config.options*
MiniBasics.setup() mini-basics.txt /*MiniBasics.setup()*
MiniBasics.toggle_diagnostic() mini-basics.txt /*MiniBasics.toggle_diagnostic()*
MiniBracketed mini-bracketed.txt /*MiniBracketed*
MiniBracketed.advance() mini-bracketed.txt /*MiniBracketed.advance()*
MiniBracketed.buffer() mini-bracketed.txt /*MiniBracketed.buffer()*
MiniBracketed.comment() mini-bracketed.txt /*MiniBracketed.comment()*
MiniBracketed.config mini-bracketed.txt /*MiniBracketed.config*
MiniBracketed.conflict() mini-bracketed.txt /*MiniBracketed.conflict()*
MiniBracketed.diagnostic() mini-bracketed.txt /*MiniBracketed.diagnostic()*
MiniBracketed.file() mini-bracketed.txt /*MiniBracketed.file()*
MiniBracketed.indent() mini-bracketed.txt /*MiniBracketed.indent()*
MiniBracketed.jump() mini-bracketed.txt /*MiniBracketed.jump()*
MiniBracketed.location() mini-bracketed.txt /*MiniBracketed.location()*
MiniBracketed.oldfile() mini-bracketed.txt /*MiniBracketed.oldfile()*
MiniBracketed.quickfix() mini-bracketed.txt /*MiniBracketed.quickfix()*
MiniBracketed.register_put_region() mini-bracketed.txt /*MiniBracketed.register_put_region()*
MiniBracketed.register_undo_state() mini-bracketed.txt /*MiniBracketed.register_undo_state()*
MiniBracketed.setup() mini-bracketed.txt /*MiniBracketed.setup()*
MiniBracketed.treesitter() mini-bracketed.txt /*MiniBracketed.treesitter()*
MiniBracketed.undo() mini-bracketed.txt /*MiniBracketed.undo()*
MiniBracketed.window() mini-bracketed.txt /*MiniBracketed.window()*
MiniBracketed.yank() mini-bracketed.txt /*MiniBracketed.yank()*
MiniBufremove mini-bufremove.txt /*MiniBufremove*
MiniBufremove.config mini-bufremove.txt /*MiniBufremove.config*
MiniBufremove.delete() mini-bufremove.txt /*MiniBufremove.delete()*
MiniBufremove.setup() mini-bufremove.txt /*MiniBufremove.setup()*
MiniBufremove.unshow() mini-bufremove.txt /*MiniBufremove.unshow()*
MiniBufremove.unshow_in_window() mini-bufremove.txt /*MiniBufremove.unshow_in_window()*
MiniBufremove.wipeout() mini-bufremove.txt /*MiniBufremove.wipeout()*
MiniClue mini-clue.txt /*MiniClue*
MiniClue-examples mini-clue.txt /*MiniClue-examples*
MiniClue-examples-submodes mini-clue.txt /*MiniClue-examples-submodes*
MiniClue-key-query-process mini-clue.txt /*MiniClue-key-query-process*
MiniClue.config mini-clue.txt /*MiniClue.config*
MiniClue.disable_all_triggers() mini-clue.txt /*MiniClue.disable_all_triggers()*
MiniClue.disable_buf_triggers() mini-clue.txt /*MiniClue.disable_buf_triggers()*
MiniClue.enable_all_triggers() mini-clue.txt /*MiniClue.enable_all_triggers()*
MiniClue.enable_buf_triggers() mini-clue.txt /*MiniClue.enable_buf_triggers()*
MiniClue.ensure_all_triggers() mini-clue.txt /*MiniClue.ensure_all_triggers()*
MiniClue.ensure_buf_triggers() mini-clue.txt /*MiniClue.ensure_buf_triggers()*
MiniClue.gen_clues mini-clue.txt /*MiniClue.gen_clues*
MiniClue.gen_clues.builtin_completion() mini-clue.txt /*MiniClue.gen_clues.builtin_completion()*
MiniClue.gen_clues.g() mini-clue.txt /*MiniClue.gen_clues.g()*
MiniClue.gen_clues.marks() mini-clue.txt /*MiniClue.gen_clues.marks()*
MiniClue.gen_clues.registers() mini-clue.txt /*MiniClue.gen_clues.registers()*
MiniClue.gen_clues.windows() mini-clue.txt /*MiniClue.gen_clues.windows()*
MiniClue.gen_clues.z() mini-clue.txt /*MiniClue.gen_clues.z()*
MiniClue.set_mapping_desc() mini-clue.txt /*MiniClue.set_mapping_desc()*
MiniClue.setup() mini-clue.txt /*MiniClue.setup()*
MiniColors mini-colors.txt /*MiniColors*
MiniColors-channels mini-colors.txt /*MiniColors-channels*
MiniColors-color-spaces mini-colors.txt /*MiniColors-color-spaces*
MiniColors-colorscheme mini-colors.txt /*MiniColors-colorscheme*
MiniColors-colorscheme-fields mini-colors.txt /*MiniColors-colorscheme-fields*
MiniColors-colorscheme-methods mini-colors.txt /*MiniColors-colorscheme-methods*
MiniColors-colorscheme:add_cterm_attributes() mini-colors.txt /*MiniColors-colorscheme:add_cterm_attributes()*
MiniColors-colorscheme:add_terminal_colors() mini-colors.txt /*MiniColors-colorscheme:add_terminal_colors()*
MiniColors-colorscheme:add_transparency() mini-colors.txt /*MiniColors-colorscheme:add_transparency()*
MiniColors-colorscheme:apply() mini-colors.txt /*MiniColors-colorscheme:apply()*
MiniColors-colorscheme:chan_add() mini-colors.txt /*MiniColors-colorscheme:chan_add()*
MiniColors-colorscheme:chan_invert() mini-colors.txt /*MiniColors-colorscheme:chan_invert()*
MiniColors-colorscheme:chan_modify() mini-colors.txt /*MiniColors-colorscheme:chan_modify()*
MiniColors-colorscheme:chan_multiply() mini-colors.txt /*MiniColors-colorscheme:chan_multiply()*
MiniColors-colorscheme:chan_repel() mini-colors.txt /*MiniColors-colorscheme:chan_repel()*
MiniColors-colorscheme:chan_set() mini-colors.txt /*MiniColors-colorscheme:chan_set()*
MiniColors-colorscheme:color_modify() mini-colors.txt /*MiniColors-colorscheme:color_modify()*
MiniColors-colorscheme:compress() mini-colors.txt /*MiniColors-colorscheme:compress()*
MiniColors-colorscheme:get_palette() mini-colors.txt /*MiniColors-colorscheme:get_palette()*
MiniColors-colorscheme:resolve_links() mini-colors.txt /*MiniColors-colorscheme:resolve_links()*
MiniColors-colorscheme:simulate_cvd() mini-colors.txt /*MiniColors-colorscheme:simulate_cvd()*
MiniColors-colorscheme:write() mini-colors.txt /*MiniColors-colorscheme:write()*
MiniColors-gamut-clip mini-colors.txt /*MiniColors-gamut-clip*
MiniColors-recipes mini-colors.txt /*MiniColors-recipes*
MiniColors.animate() mini-colors.txt /*MiniColors.animate()*
MiniColors.as_colorscheme() mini-colors.txt /*MiniColors.as_colorscheme()*
MiniColors.config mini-colors.txt /*MiniColors.config*
MiniColors.convert() mini-colors.txt /*MiniColors.convert()*
MiniColors.get_colorscheme() mini-colors.txt /*MiniColors.get_colorscheme()*
MiniColors.interactive() mini-colors.txt /*MiniColors.interactive()*
MiniColors.modify_channel() mini-colors.txt /*MiniColors.modify_channel()*
MiniColors.setup() mini-colors.txt /*MiniColors.setup()*
MiniColors.simulate_cvd() mini-colors.txt /*MiniColors.simulate_cvd()*
MiniComment mini-comment.txt /*MiniComment*
MiniComment.config mini-comment.txt /*MiniComment.config*
MiniComment.get_commentstring() mini-comment.txt /*MiniComment.get_commentstring()*
MiniComment.operator() mini-comment.txt /*MiniComment.operator()*
MiniComment.setup() mini-comment.txt /*MiniComment.setup()*
MiniComment.textobject() mini-comment.txt /*MiniComment.textobject()*
MiniComment.toggle_lines() mini-comment.txt /*MiniComment.toggle_lines()*
MiniCompletion mini-completion.txt /*MiniCompletion*
MiniCompletion.complete_fallback() mini-completion.txt /*MiniCompletion.complete_fallback()*
MiniCompletion.complete_twostage() mini-completion.txt /*MiniCompletion.complete_twostage()*
MiniCompletion.completefunc_lsp() mini-completion.txt /*MiniCompletion.completefunc_lsp()*
MiniCompletion.config mini-completion.txt /*MiniCompletion.config*
MiniCompletion.default_process_items() mini-completion.txt /*MiniCompletion.default_process_items()*
MiniCompletion.setup() mini-completion.txt /*MiniCompletion.setup()*
MiniCompletion.stop() mini-completion.txt /*MiniCompletion.stop()*
MiniCursorword mini-cursorword.txt /*MiniCursorword*
MiniCursorword.config mini-cursorword.txt /*MiniCursorword.config*
MiniCursorword.setup() mini-cursorword.txt /*MiniCursorword.setup()*
MiniDeps mini-deps.txt /*MiniDeps*
MiniDeps-commands mini-deps.txt /*MiniDeps-commands*
MiniDeps-overview mini-deps.txt /*MiniDeps-overview*
MiniDeps-plugin-specification mini-deps.txt /*MiniDeps-plugin-specification*
MiniDeps.add() mini-deps.txt /*MiniDeps.add()*
MiniDeps.clean() mini-deps.txt /*MiniDeps.clean()*
MiniDeps.config mini-deps.txt /*MiniDeps.config*
MiniDeps.get_session() mini-deps.txt /*MiniDeps.get_session()*
MiniDeps.later() mini-deps.txt /*MiniDeps.later()*
MiniDeps.now() mini-deps.txt /*MiniDeps.now()*
MiniDeps.setup() mini-deps.txt /*MiniDeps.setup()*
MiniDeps.snap_get() mini-deps.txt /*MiniDeps.snap_get()*
MiniDeps.snap_load() mini-deps.txt /*MiniDeps.snap_load()*
MiniDeps.snap_save() mini-deps.txt /*MiniDeps.snap_save()*
MiniDeps.snap_set() mini-deps.txt /*MiniDeps.snap_set()*
MiniDeps.update() mini-deps.txt /*MiniDeps.update()*
MiniDiff mini-diff.txt /*MiniDiff*
MiniDiff-diff-summary mini-diff.txt /*MiniDiff-diff-summary*
MiniDiff-hunk-specification mini-diff.txt /*MiniDiff-hunk-specification*
MiniDiff-overview mini-diff.txt /*MiniDiff-overview*
MiniDiff-source-specification mini-diff.txt /*MiniDiff-source-specification*
MiniDiff-update-event mini-diff.txt /*MiniDiff-update-event*
MiniDiff.config mini-diff.txt /*MiniDiff.config*
MiniDiff.disable() mini-diff.txt /*MiniDiff.disable()*
MiniDiff.do_hunks() mini-diff.txt /*MiniDiff.do_hunks()*
MiniDiff.enable() mini-diff.txt /*MiniDiff.enable()*
MiniDiff.export() mini-diff.txt /*MiniDiff.export()*
MiniDiff.gen_source mini-diff.txt /*MiniDiff.gen_source*
MiniDiff.gen_source.git() mini-diff.txt /*MiniDiff.gen_source.git()*
MiniDiff.gen_source.none() mini-diff.txt /*MiniDiff.gen_source.none()*
MiniDiff.gen_source.save() mini-diff.txt /*MiniDiff.gen_source.save()*
MiniDiff.get_buf_data() mini-diff.txt /*MiniDiff.get_buf_data()*
MiniDiff.goto_hunk() mini-diff.txt /*MiniDiff.goto_hunk()*
MiniDiff.operator() mini-diff.txt /*MiniDiff.operator()*
MiniDiff.set_ref_text() mini-diff.txt /*MiniDiff.set_ref_text()*
MiniDiff.setup() mini-diff.txt /*MiniDiff.setup()*
MiniDiff.textobject() mini-diff.txt /*MiniDiff.textobject()*
MiniDiff.toggle() mini-diff.txt /*MiniDiff.toggle()*
MiniDiff.toggle_overlay() mini-diff.txt /*MiniDiff.toggle_overlay()*
MiniDoc mini-doc.txt /*MiniDoc*
MiniDoc-data-structures mini-doc.txt /*MiniDoc-data-structures*
MiniDoc.afterlines_to_code() mini-doc.txt /*MiniDoc.afterlines_to_code()*
MiniDoc.config mini-doc.txt /*MiniDoc.config*
MiniDoc.current mini-doc.txt /*MiniDoc.current*
MiniDoc.default_hooks mini-doc.txt /*MiniDoc.default_hooks*
MiniDoc.generate() mini-doc.txt /*MiniDoc.generate()*
MiniDoc.setup() mini-doc.txt /*MiniDoc.setup()*
MiniExtra mini-extra.txt /*MiniExtra*
MiniExtra.config mini-extra.txt /*MiniExtra.config*
MiniExtra.gen_ai_spec mini-extra.txt /*MiniExtra.gen_ai_spec*
MiniExtra.gen_ai_spec.buffer() mini-extra.txt /*MiniExtra.gen_ai_spec.buffer()*
MiniExtra.gen_ai_spec.diagnostic() mini-extra.txt /*MiniExtra.gen_ai_spec.diagnostic()*
MiniExtra.gen_ai_spec.indent() mini-extra.txt /*MiniExtra.gen_ai_spec.indent()*
MiniExtra.gen_ai_spec.line() mini-extra.txt /*MiniExtra.gen_ai_spec.line()*
MiniExtra.gen_ai_spec.number() mini-extra.txt /*MiniExtra.gen_ai_spec.number()*
MiniExtra.gen_highlighter mini-extra.txt /*MiniExtra.gen_highlighter*
MiniExtra.gen_highlighter.words() mini-extra.txt /*MiniExtra.gen_highlighter.words()*
MiniExtra.pickers mini-extra.txt /*MiniExtra.pickers*
MiniExtra.pickers.buf_lines() mini-extra.txt /*MiniExtra.pickers.buf_lines()*
MiniExtra.pickers.commands() mini-extra.txt /*MiniExtra.pickers.commands()*
MiniExtra.pickers.diagnostic() mini-extra.txt /*MiniExtra.pickers.diagnostic()*
MiniExtra.pickers.explorer() mini-extra.txt /*MiniExtra.pickers.explorer()*
MiniExtra.pickers.git_branches() mini-extra.txt /*MiniExtra.pickers.git_branches()*
MiniExtra.pickers.git_commits() mini-extra.txt /*MiniExtra.pickers.git_commits()*
MiniExtra.pickers.git_files() mini-extra.txt /*MiniExtra.pickers.git_files()*
MiniExtra.pickers.git_hunks() mini-extra.txt /*MiniExtra.pickers.git_hunks()*
MiniExtra.pickers.hipatterns() mini-extra.txt /*MiniExtra.pickers.hipatterns()*
MiniExtra.pickers.history() mini-extra.txt /*MiniExtra.pickers.history()*
MiniExtra.pickers.hl_groups() mini-extra.txt /*MiniExtra.pickers.hl_groups()*
MiniExtra.pickers.keymaps() mini-extra.txt /*MiniExtra.pickers.keymaps()*
MiniExtra.pickers.list() mini-extra.txt /*MiniExtra.pickers.list()*
MiniExtra.pickers.lsp() mini-extra.txt /*MiniExtra.pickers.lsp()*
MiniExtra.pickers.marks() mini-extra.txt /*MiniExtra.pickers.marks()*
MiniExtra.pickers.oldfiles() mini-extra.txt /*MiniExtra.pickers.oldfiles()*
MiniExtra.pickers.options() mini-extra.txt /*MiniExtra.pickers.options()*
MiniExtra.pickers.registers() mini-extra.txt /*MiniExtra.pickers.registers()*
MiniExtra.pickers.spellsuggest() mini-extra.txt /*MiniExtra.pickers.spellsuggest()*
MiniExtra.pickers.treesitter() mini-extra.txt /*MiniExtra.pickers.treesitter()*
MiniExtra.pickers.visit_labels() mini-extra.txt /*MiniExtra.pickers.visit_labels()*
MiniExtra.pickers.visit_paths() mini-extra.txt /*MiniExtra.pickers.visit_paths()*
MiniExtra.setup() mini-extra.txt /*MiniExtra.setup()*
MiniFiles mini-files.txt /*MiniFiles*
MiniFiles-events mini-files.txt /*MiniFiles-events*
MiniFiles-examples mini-files.txt /*MiniFiles-examples*
MiniFiles-manipulation mini-files.txt /*MiniFiles-manipulation*
MiniFiles-navigation mini-files.txt /*MiniFiles-navigation*
MiniFiles.close() mini-files.txt /*MiniFiles.close()*
MiniFiles.config mini-files.txt /*MiniFiles.config*
MiniFiles.default_filter() mini-files.txt /*MiniFiles.default_filter()*
MiniFiles.default_prefix() mini-files.txt /*MiniFiles.default_prefix()*
MiniFiles.default_sort() mini-files.txt /*MiniFiles.default_sort()*
MiniFiles.get_fs_entry() mini-files.txt /*MiniFiles.get_fs_entry()*
MiniFiles.get_latest_path() mini-files.txt /*MiniFiles.get_latest_path()*
MiniFiles.get_target_window() mini-files.txt /*MiniFiles.get_target_window()*
MiniFiles.go_in() mini-files.txt /*MiniFiles.go_in()*
MiniFiles.go_out() mini-files.txt /*MiniFiles.go_out()*
MiniFiles.open() mini-files.txt /*MiniFiles.open()*
MiniFiles.refresh() mini-files.txt /*MiniFiles.refresh()*
MiniFiles.reset() mini-files.txt /*MiniFiles.reset()*
MiniFiles.reveal_cwd() mini-files.txt /*MiniFiles.reveal_cwd()*
MiniFiles.set_target_window() mini-files.txt /*MiniFiles.set_target_window()*
MiniFiles.setup() mini-files.txt /*MiniFiles.setup()*
MiniFiles.show_help() mini-files.txt /*MiniFiles.show_help()*
MiniFiles.synchronize() mini-files.txt /*MiniFiles.synchronize()*
MiniFiles.trim_left() mini-files.txt /*MiniFiles.trim_left()*
MiniFiles.trim_right() mini-files.txt /*MiniFiles.trim_right()*
MiniFuzzy mini-fuzzy.txt /*MiniFuzzy*
MiniFuzzy-algorithm mini-fuzzy.txt /*MiniFuzzy-algorithm*
MiniFuzzy.config mini-fuzzy.txt /*MiniFuzzy.config*
MiniFuzzy.filtersort() mini-fuzzy.txt /*MiniFuzzy.filtersort()*
MiniFuzzy.get_telescope_sorter() mini-fuzzy.txt /*MiniFuzzy.get_telescope_sorter()*
MiniFuzzy.match() mini-fuzzy.txt /*MiniFuzzy.match()*
MiniFuzzy.process_lsp_items() mini-fuzzy.txt /*MiniFuzzy.process_lsp_items()*
MiniFuzzy.setup() mini-fuzzy.txt /*MiniFuzzy.setup()*
MiniGit mini-git.txt /*MiniGit*
MiniGit-command mini-git.txt /*MiniGit-command*
MiniGit-command-events mini-git.txt /*MiniGit-command-events*
MiniGit-examples mini-git.txt /*MiniGit-examples*
MiniGit.config mini-git.txt /*MiniGit.config*
MiniGit.diff_foldexpr() mini-git.txt /*MiniGit.diff_foldexpr()*
MiniGit.disable() mini-git.txt /*MiniGit.disable()*
MiniGit.enable() mini-git.txt /*MiniGit.enable()*
MiniGit.get_buf_data() mini-git.txt /*MiniGit.get_buf_data()*
MiniGit.setup() mini-git.txt /*MiniGit.setup()*
MiniGit.show_at_cursor() mini-git.txt /*MiniGit.show_at_cursor()*
MiniGit.show_diff_source() mini-git.txt /*MiniGit.show_diff_source()*
MiniGit.show_range_history() mini-git.txt /*MiniGit.show_range_history()*
MiniGit.toggle() mini-git.txt /*MiniGit.toggle()*
MiniHipatterns mini-hipatterns.txt /*MiniHipatterns*
MiniHipatterns-examples mini-hipatterns.txt /*MiniHipatterns-examples*
MiniHipatterns.compute_hex_color_group() mini-hipatterns.txt /*MiniHipatterns.compute_hex_color_group()*
MiniHipatterns.config mini-hipatterns.txt /*MiniHipatterns.config*
MiniHipatterns.disable() mini-hipatterns.txt /*MiniHipatterns.disable()*
MiniHipatterns.enable() mini-hipatterns.txt /*MiniHipatterns.enable()*
MiniHipatterns.gen_highlighter mini-hipatterns.txt /*MiniHipatterns.gen_highlighter*
MiniHipatterns.gen_highlighter.hex_color() mini-hipatterns.txt /*MiniHipatterns.gen_highlighter.hex_color()*
MiniHipatterns.get_enabled_buffers() mini-hipatterns.txt /*MiniHipatterns.get_enabled_buffers()*
MiniHipatterns.get_matches() mini-hipatterns.txt /*MiniHipatterns.get_matches()*
MiniHipatterns.setup() mini-hipatterns.txt /*MiniHipatterns.setup()*
MiniHipatterns.toggle() mini-hipatterns.txt /*MiniHipatterns.toggle()*
MiniHipatterns.update() mini-hipatterns.txt /*MiniHipatterns.update()*
MiniHues mini-hues.txt /*MiniHues*
MiniHues.apply_palette() mini-hues.txt /*MiniHues.apply_palette()*
MiniHues.config mini-hues.txt /*MiniHues.config*
MiniHues.gen_random_base_colors() mini-hues.txt /*MiniHues.gen_random_base_colors()*
MiniHues.make_palette() mini-hues.txt /*MiniHues.make_palette()*
MiniHues.setup() mini-hues.txt /*MiniHues.setup()*
MiniIcons mini-icons.txt /*MiniIcons*
MiniIcons.config mini-icons.txt /*MiniIcons.config*
MiniIcons.get() mini-icons.txt /*MiniIcons.get()*
MiniIcons.list() mini-icons.txt /*MiniIcons.list()*
MiniIcons.mock_nvim_web_devicons() mini-icons.txt /*MiniIcons.mock_nvim_web_devicons()*
MiniIcons.setup() mini-icons.txt /*MiniIcons.setup()*
MiniIcons.tweak_lsp_kind() mini-icons.txt /*MiniIcons.tweak_lsp_kind()*
MiniIndentscope mini-indentscope.txt /*MiniIndentscope*
MiniIndentscope-drawing mini-indentscope.txt /*MiniIndentscope-drawing*
MiniIndentscope.config mini-indentscope.txt /*MiniIndentscope.config*
MiniIndentscope.draw() mini-indentscope.txt /*MiniIndentscope.draw()*
MiniIndentscope.gen_animation mini-indentscope.txt /*MiniIndentscope.gen_animation*
MiniIndentscope.gen_animation.cubic() mini-indentscope.txt /*MiniIndentscope.gen_animation.cubic()*
MiniIndentscope.gen_animation.exponential() mini-indentscope.txt /*MiniIndentscope.gen_animation.exponential()*
MiniIndentscope.gen_animation.linear() mini-indentscope.txt /*MiniIndentscope.gen_animation.linear()*
MiniIndentscope.gen_animation.none() mini-indentscope.txt /*MiniIndentscope.gen_animation.none()*
MiniIndentscope.gen_animation.quadratic() mini-indentscope.txt /*MiniIndentscope.gen_animation.quadratic()*
MiniIndentscope.gen_animation.quartic() mini-indentscope.txt /*MiniIndentscope.gen_animation.quartic()*
MiniIndentscope.get_scope() mini-indentscope.txt /*MiniIndentscope.get_scope()*
MiniIndentscope.move_cursor() mini-indentscope.txt /*MiniIndentscope.move_cursor()*
MiniIndentscope.operator() mini-indentscope.txt /*MiniIndentscope.operator()*
MiniIndentscope.setup() mini-indentscope.txt /*MiniIndentscope.setup()*
MiniIndentscope.textobject() mini-indentscope.txt /*MiniIndentscope.textobject()*
MiniIndentscope.undraw() mini-indentscope.txt /*MiniIndentscope.undraw()*
MiniJump mini-jump.txt /*MiniJump*
MiniJump.config mini-jump.txt /*MiniJump.config*
MiniJump.jump() mini-jump.txt /*MiniJump.jump()*
MiniJump.setup() mini-jump.txt /*MiniJump.setup()*
MiniJump.smart_jump() mini-jump.txt /*MiniJump.smart_jump()*
MiniJump.state mini-jump.txt /*MiniJump.state*
MiniJump.stop_jumping() mini-jump.txt /*MiniJump.stop_jumping()*
MiniJump2d mini-jump2d.txt /*MiniJump2d*
MiniJump2d.builtin_opts mini-jump2d.txt /*MiniJump2d.builtin_opts*
MiniJump2d.builtin_opts.default mini-jump2d.txt /*MiniJump2d.builtin_opts.default*
MiniJump2d.builtin_opts.line_start mini-jump2d.txt /*MiniJump2d.builtin_opts.line_start*
MiniJump2d.builtin_opts.query mini-jump2d.txt /*MiniJump2d.builtin_opts.query*
MiniJump2d.builtin_opts.single_character mini-jump2d.txt /*MiniJump2d.builtin_opts.single_character*
MiniJump2d.builtin_opts.word_start mini-jump2d.txt /*MiniJump2d.builtin_opts.word_start*
MiniJump2d.config mini-jump2d.txt /*MiniJump2d.config*
MiniJump2d.default_spotter mini-jump2d.txt /*MiniJump2d.default_spotter*
MiniJump2d.gen_pattern_spotter() mini-jump2d.txt /*MiniJump2d.gen_pattern_spotter()*
MiniJump2d.gen_union_spotter() mini-jump2d.txt /*MiniJump2d.gen_union_spotter()*
MiniJump2d.setup() mini-jump2d.txt /*MiniJump2d.setup()*
MiniJump2d.start() mini-jump2d.txt /*MiniJump2d.start()*
MiniJump2d.stop() mini-jump2d.txt /*MiniJump2d.stop()*
MiniMap mini-map.txt /*MiniMap*
MiniMap.close() mini-map.txt /*MiniMap.close()*
MiniMap.config mini-map.txt /*MiniMap.config*
MiniMap.current mini-map.txt /*MiniMap.current*
MiniMap.encode_strings() mini-map.txt /*MiniMap.encode_strings()*
MiniMap.gen_encode_symbols mini-map.txt /*MiniMap.gen_encode_symbols*
MiniMap.gen_encode_symbols.block() mini-map.txt /*MiniMap.gen_encode_symbols.block()*
MiniMap.gen_encode_symbols.dot() mini-map.txt /*MiniMap.gen_encode_symbols.dot()*
MiniMap.gen_encode_symbols.shade() mini-map.txt /*MiniMap.gen_encode_symbols.shade()*
MiniMap.gen_integration mini-map.txt /*MiniMap.gen_integration*
MiniMap.gen_integration.builtin_search() mini-map.txt /*MiniMap.gen_integration.builtin_search()*
MiniMap.gen_integration.diagnostic() mini-map.txt /*MiniMap.gen_integration.diagnostic()*
MiniMap.gen_integration.diff() mini-map.txt /*MiniMap.gen_integration.diff()*
MiniMap.gen_integration.gitsigns() mini-map.txt /*MiniMap.gen_integration.gitsigns()*
MiniMap.open() mini-map.txt /*MiniMap.open()*
MiniMap.refresh() mini-map.txt /*MiniMap.refresh()*
MiniMap.setup() mini-map.txt /*MiniMap.setup()*
MiniMap.toggle() mini-map.txt /*MiniMap.toggle()*
MiniMap.toggle_focus() mini-map.txt /*MiniMap.toggle_focus()*
MiniMap.toggle_side() mini-map.txt /*MiniMap.toggle_side()*
MiniMisc mini-misc.txt /*MiniMisc*
MiniMisc.bench_time() mini-misc.txt /*MiniMisc.bench_time()*
MiniMisc.config mini-misc.txt /*MiniMisc.config*
MiniMisc.find_root() mini-misc.txt /*MiniMisc.find_root()*
MiniMisc.get_gutter_width() mini-misc.txt /*MiniMisc.get_gutter_width()*
MiniMisc.put() mini-misc.txt /*MiniMisc.put()*
MiniMisc.put_text() mini-misc.txt /*MiniMisc.put_text()*
MiniMisc.resize_window() mini-misc.txt /*MiniMisc.resize_window()*
MiniMisc.setup() mini-misc.txt /*MiniMisc.setup()*
MiniMisc.setup_auto_root() mini-misc.txt /*MiniMisc.setup_auto_root()*
MiniMisc.setup_restore_cursor() mini-misc.txt /*MiniMisc.setup_restore_cursor()*
MiniMisc.setup_termbg_sync() mini-misc.txt /*MiniMisc.setup_termbg_sync()*
MiniMisc.stat_summary() mini-misc.txt /*MiniMisc.stat_summary()*
MiniMisc.tbl_head() mini-misc.txt /*MiniMisc.tbl_head()*
MiniMisc.tbl_tail() mini-misc.txt /*MiniMisc.tbl_tail()*
MiniMisc.use_nested_comments() mini-misc.txt /*MiniMisc.use_nested_comments()*
MiniMisc.zoom() mini-misc.txt /*MiniMisc.zoom()*
MiniMove mini-move.txt /*MiniMove*
MiniMove.config mini-move.txt /*MiniMove.config*
MiniMove.move_line() mini-move.txt /*MiniMove.move_line()*
MiniMove.move_selection() mini-move.txt /*MiniMove.move_selection()*
MiniMove.setup() mini-move.txt /*MiniMove.setup()*
MiniNotify mini-notify.txt /*MiniNotify*
MiniNotify-specification mini-notify.txt /*MiniNotify-specification*
MiniNotify.add() mini-notify.txt /*MiniNotify.add()*
MiniNotify.clear() mini-notify.txt /*MiniNotify.clear()*
MiniNotify.config mini-notify.txt /*MiniNotify.config*
MiniNotify.default_format() mini-notify.txt /*MiniNotify.default_format()*
MiniNotify.default_sort() mini-notify.txt /*MiniNotify.default_sort()*
MiniNotify.get() mini-notify.txt /*MiniNotify.get()*
MiniNotify.get_all() mini-notify.txt /*MiniNotify.get_all()*
MiniNotify.make_notify() mini-notify.txt /*MiniNotify.make_notify()*
MiniNotify.refresh() mini-notify.txt /*MiniNotify.refresh()*
MiniNotify.remove() mini-notify.txt /*MiniNotify.remove()*
MiniNotify.setup() mini-notify.txt /*MiniNotify.setup()*
MiniNotify.show_history() mini-notify.txt /*MiniNotify.show_history()*
MiniNotify.update() mini-notify.txt /*MiniNotify.update()*
MiniOperators mini-operators.txt /*MiniOperators*
MiniOperators-mappings mini-operators.txt /*MiniOperators-mappings*
MiniOperators-overview mini-operators.txt /*MiniOperators-overview*
MiniOperators.config mini-operators.txt /*MiniOperators.config*
MiniOperators.default_evaluate_func() mini-operators.txt /*MiniOperators.default_evaluate_func()*
MiniOperators.default_sort_func() mini-operators.txt /*MiniOperators.default_sort_func()*
MiniOperators.evaluate() mini-operators.txt /*MiniOperators.evaluate()*
MiniOperators.exchange() mini-operators.txt /*MiniOperators.exchange()*
MiniOperators.make_mappings() mini-operators.txt /*MiniOperators.make_mappings()*
MiniOperators.multiply() mini-operators.txt /*MiniOperators.multiply()*
MiniOperators.replace() mini-operators.txt /*MiniOperators.replace()*
MiniOperators.setup() mini-operators.txt /*MiniOperators.setup()*
MiniOperators.sort() mini-operators.txt /*MiniOperators.sort()*
MiniPairs mini-pairs.txt /*MiniPairs*
MiniPairs.bs() mini-pairs.txt /*MiniPairs.bs()*
MiniPairs.close() mini-pairs.txt /*MiniPairs.close()*
MiniPairs.closeopen() mini-pairs.txt /*MiniPairs.closeopen()*
MiniPairs.config mini-pairs.txt /*MiniPairs.config*
MiniPairs.cr() mini-pairs.txt /*MiniPairs.cr()*
MiniPairs.map() mini-pairs.txt /*MiniPairs.map()*
MiniPairs.map_buf() mini-pairs.txt /*MiniPairs.map_buf()*
MiniPairs.open() mini-pairs.txt /*MiniPairs.open()*
MiniPairs.setup() mini-pairs.txt /*MiniPairs.setup()*
MiniPairs.unmap() mini-pairs.txt /*MiniPairs.unmap()*
MiniPairs.unmap_buf() mini-pairs.txt /*MiniPairs.unmap_buf()*
MiniPick mini-pick.txt /*MiniPick*
MiniPick-actions mini-pick.txt /*MiniPick-actions*
MiniPick-actions-caret mini-pick.txt /*MiniPick-actions-caret*
MiniPick-actions-choose mini-pick.txt /*MiniPick-actions-choose*
MiniPick-actions-custom mini-pick.txt /*MiniPick-actions-custom*
MiniPick-actions-delete mini-pick.txt /*MiniPick-actions-delete*
MiniPick-actions-mark mini-pick.txt /*MiniPick-actions-mark*
MiniPick-actions-move mini-pick.txt /*MiniPick-actions-move*
MiniPick-actions-paste mini-pick.txt /*MiniPick-actions-paste*
MiniPick-actions-refine mini-pick.txt /*MiniPick-actions-refine*
MiniPick-actions-scroll mini-pick.txt /*MiniPick-actions-scroll*
MiniPick-actions-stop mini-pick.txt /*MiniPick-actions-stop*
MiniPick-actions-toggle mini-pick.txt /*MiniPick-actions-toggle*
MiniPick-cli-tools mini-pick.txt /*MiniPick-cli-tools*
MiniPick-events mini-pick.txt /*MiniPick-events*
MiniPick-examples mini-pick.txt /*MiniPick-examples*
MiniPick-overview mini-pick.txt /*MiniPick-overview*
MiniPick-source mini-pick.txt /*MiniPick-source*
MiniPick-source.choose mini-pick.txt /*MiniPick-source.choose*
MiniPick-source.choose_marked mini-pick.txt /*MiniPick-source.choose_marked*
MiniPick-source.cwd mini-pick.txt /*MiniPick-source.cwd*
MiniPick-source.items mini-pick.txt /*MiniPick-source.items*
MiniPick-source.items-common mini-pick.txt /*MiniPick-source.items-common*
MiniPick-source.items-stritems mini-pick.txt /*MiniPick-source.items-stritems*
MiniPick-source.match mini-pick.txt /*MiniPick-source.match*
MiniPick-source.name mini-pick.txt /*MiniPick-source.name*
MiniPick-source.preview mini-pick.txt /*MiniPick-source.preview*
MiniPick-source.show mini-pick.txt /*MiniPick-source.show*
MiniPick.builtin mini-pick.txt /*MiniPick.builtin*
MiniPick.builtin.buffers() mini-pick.txt /*MiniPick.builtin.buffers()*
MiniPick.builtin.cli() mini-pick.txt /*MiniPick.builtin.cli()*
MiniPick.builtin.files() mini-pick.txt /*MiniPick.builtin.files()*
MiniPick.builtin.grep() mini-pick.txt /*MiniPick.builtin.grep()*
MiniPick.builtin.grep_live() mini-pick.txt /*MiniPick.builtin.grep_live()*
MiniPick.builtin.help() mini-pick.txt /*MiniPick.builtin.help()*
MiniPick.builtin.resume() mini-pick.txt /*MiniPick.builtin.resume()*
MiniPick.config mini-pick.txt /*MiniPick.config*
MiniPick.default_choose() mini-pick.txt /*MiniPick.default_choose()*
MiniPick.default_choose_marked() mini-pick.txt /*MiniPick.default_choose_marked()*
MiniPick.default_match() mini-pick.txt /*MiniPick.default_match()*
MiniPick.default_preview() mini-pick.txt /*MiniPick.default_preview()*
MiniPick.default_show() mini-pick.txt /*MiniPick.default_show()*
MiniPick.get_picker_items() mini-pick.txt /*MiniPick.get_picker_items()*
MiniPick.get_picker_matches() mini-pick.txt /*MiniPick.get_picker_matches()*
MiniPick.get_picker_opts() mini-pick.txt /*MiniPick.get_picker_opts()*
MiniPick.get_picker_query() mini-pick.txt /*MiniPick.get_picker_query()*
MiniPick.get_picker_state() mini-pick.txt /*MiniPick.get_picker_state()*
MiniPick.get_picker_stritems() mini-pick.txt /*MiniPick.get_picker_stritems()*
MiniPick.get_querytick() mini-pick.txt /*MiniPick.get_querytick()*
MiniPick.is_picker_active() mini-pick.txt /*MiniPick.is_picker_active()*
MiniPick.poke_is_picker_active() mini-pick.txt /*MiniPick.poke_is_picker_active()*
MiniPick.refresh() mini-pick.txt /*MiniPick.refresh()*
MiniPick.registry mini-pick.txt /*MiniPick.registry*
MiniPick.set_picker_items() mini-pick.txt /*MiniPick.set_picker_items()*
MiniPick.set_picker_items_from_cli() mini-pick.txt /*MiniPick.set_picker_items_from_cli()*
MiniPick.set_picker_match_inds() mini-pick.txt /*MiniPick.set_picker_match_inds()*
MiniPick.set_picker_opts() mini-pick.txt /*MiniPick.set_picker_opts()*
MiniPick.set_picker_query() mini-pick.txt /*MiniPick.set_picker_query()*
MiniPick.set_picker_target_window() mini-pick.txt /*MiniPick.set_picker_target_window()*
MiniPick.setup() mini-pick.txt /*MiniPick.setup()*
MiniPick.start() mini-pick.txt /*MiniPick.start()*
MiniPick.stop() mini-pick.txt /*MiniPick.stop()*
MiniPick.ui_select() mini-pick.txt /*MiniPick.ui_select()*
MiniSessions mini-sessions.txt /*MiniSessions*
MiniSessions.config mini-sessions.txt /*MiniSessions.config*
MiniSessions.delete() mini-sessions.txt /*MiniSessions.delete()*
MiniSessions.detected mini-sessions.txt /*MiniSessions.detected*
MiniSessions.get_latest() mini-sessions.txt /*MiniSessions.get_latest()*
MiniSessions.read() mini-sessions.txt /*MiniSessions.read()*
MiniSessions.select() mini-sessions.txt /*MiniSessions.select()*
MiniSessions.setup() mini-sessions.txt /*MiniSessions.setup()*
MiniSessions.write() mini-sessions.txt /*MiniSessions.write()*
MiniSplitjoin mini-splitjoin.txt /*MiniSplitjoin*
MiniSplitjoin-glossary mini-splitjoin.txt /*MiniSplitjoin-glossary*
MiniSplitjoin.config mini-splitjoin.txt /*MiniSplitjoin.config*
MiniSplitjoin.config.detect mini-splitjoin.txt /*MiniSplitjoin.config.detect*
MiniSplitjoin.gen_hook mini-splitjoin.txt /*MiniSplitjoin.gen_hook*
MiniSplitjoin.gen_hook.add_trailing_separator() mini-splitjoin.txt /*MiniSplitjoin.gen_hook.add_trailing_separator()*
MiniSplitjoin.gen_hook.del_trailing_separator() mini-splitjoin.txt /*MiniSplitjoin.gen_hook.del_trailing_separator()*
MiniSplitjoin.gen_hook.pad_brackets() mini-splitjoin.txt /*MiniSplitjoin.gen_hook.pad_brackets()*
MiniSplitjoin.get_indent_part() mini-splitjoin.txt /*MiniSplitjoin.get_indent_part()*
MiniSplitjoin.get_visual_region() mini-splitjoin.txt /*MiniSplitjoin.get_visual_region()*
MiniSplitjoin.join() mini-splitjoin.txt /*MiniSplitjoin.join()*
MiniSplitjoin.join_at() mini-splitjoin.txt /*MiniSplitjoin.join_at()*
MiniSplitjoin.operator() mini-splitjoin.txt /*MiniSplitjoin.operator()*
MiniSplitjoin.setup() mini-splitjoin.txt /*MiniSplitjoin.setup()*
MiniSplitjoin.split() mini-splitjoin.txt /*MiniSplitjoin.split()*
MiniSplitjoin.split_at() mini-splitjoin.txt /*MiniSplitjoin.split_at()*
MiniSplitjoin.toggle() mini-splitjoin.txt /*MiniSplitjoin.toggle()*
MiniStarter mini-starter.txt /*MiniStarter*
MiniStarter-example-config mini-starter.txt /*MiniStarter-example-config*
MiniStarter-lifecycle mini-starter.txt /*MiniStarter-lifecycle*
MiniStarter.add_to_query() mini-starter.txt /*MiniStarter.add_to_query()*
MiniStarter.close() mini-starter.txt /*MiniStarter.close()*
MiniStarter.config mini-starter.txt /*MiniStarter.config*
MiniStarter.content_coords() mini-starter.txt /*MiniStarter.content_coords()*
MiniStarter.content_to_items() mini-starter.txt /*MiniStarter.content_to_items()*
MiniStarter.content_to_lines() mini-starter.txt /*MiniStarter.content_to_lines()*
MiniStarter.eval_current_item() mini-starter.txt /*MiniStarter.eval_current_item()*
MiniStarter.gen_hook mini-starter.txt /*MiniStarter.gen_hook*
MiniStarter.gen_hook.adding_bullet() mini-starter.txt /*MiniStarter.gen_hook.adding_bullet()*
MiniStarter.gen_hook.aligning() mini-starter.txt /*MiniStarter.gen_hook.aligning()*
MiniStarter.gen_hook.indexing() mini-starter.txt /*MiniStarter.gen_hook.indexing()*
MiniStarter.gen_hook.padding() mini-starter.txt /*MiniStarter.gen_hook.padding()*
MiniStarter.get_content() mini-starter.txt /*MiniStarter.get_content()*
MiniStarter.open() mini-starter.txt /*MiniStarter.open()*
MiniStarter.refresh() mini-starter.txt /*MiniStarter.refresh()*
MiniStarter.sections mini-starter.txt /*MiniStarter.sections*
MiniStarter.sections.builtin_actions() mini-starter.txt /*MiniStarter.sections.builtin_actions()*
MiniStarter.sections.pick() mini-starter.txt /*MiniStarter.sections.pick()*
MiniStarter.sections.recent_files() mini-starter.txt /*MiniStarter.sections.recent_files()*
MiniStarter.sections.sessions() mini-starter.txt /*MiniStarter.sections.sessions()*
MiniStarter.sections.telescope() mini-starter.txt /*MiniStarter.sections.telescope()*
MiniStarter.set_query() mini-starter.txt /*MiniStarter.set_query()*
MiniStarter.setup() mini-starter.txt /*MiniStarter.setup()*
MiniStarter.update_current_item() mini-starter.txt /*MiniStarter.update_current_item()*
MiniStatusline mini-statusline.txt /*MiniStatusline*
MiniStatusline-example-content mini-statusline.txt /*MiniStatusline-example-content*
MiniStatusline.active() mini-statusline.txt /*MiniStatusline.active()*
MiniStatusline.combine_groups() mini-statusline.txt /*MiniStatusline.combine_groups()*
MiniStatusline.config mini-statusline.txt /*MiniStatusline.config*
MiniStatusline.inactive() mini-statusline.txt /*MiniStatusline.inactive()*
MiniStatusline.is_truncated() mini-statusline.txt /*MiniStatusline.is_truncated()*
MiniStatusline.section_diagnostics() mini-statusline.txt /*MiniStatusline.section_diagnostics()*
MiniStatusline.section_diff() mini-statusline.txt /*MiniStatusline.section_diff()*
MiniStatusline.section_fileinfo() mini-statusline.txt /*MiniStatusline.section_fileinfo()*
MiniStatusline.section_filename() mini-statusline.txt /*MiniStatusline.section_filename()*
MiniStatusline.section_git() mini-statusline.txt /*MiniStatusline.section_git()*
MiniStatusline.section_location() mini-statusline.txt /*MiniStatusline.section_location()*
MiniStatusline.section_lsp() mini-statusline.txt /*MiniStatusline.section_lsp()*
MiniStatusline.section_mode() mini-statusline.txt /*MiniStatusline.section_mode()*
MiniStatusline.section_searchcount() mini-statusline.txt /*MiniStatusline.section_searchcount()*
MiniStatusline.setup() mini-statusline.txt /*MiniStatusline.setup()*
MiniSurround mini-surround.txt /*MiniSurround*
MiniSurround-count mini-surround.txt /*MiniSurround-count*
MiniSurround-glossary mini-surround.txt /*MiniSurround-glossary*
MiniSurround-search-algorithm mini-surround.txt /*MiniSurround-search-algorithm*
MiniSurround-surround-builtin mini-surround.txt /*MiniSurround-surround-builtin*
MiniSurround-surround-specification mini-surround.txt /*MiniSurround-surround-specification*
MiniSurround-vim-surround-config mini-surround.txt /*MiniSurround-vim-surround-config*
MiniSurround.add() mini-surround.txt /*MiniSurround.add()*
MiniSurround.config mini-surround.txt /*MiniSurround.config*
MiniSurround.delete() mini-surround.txt /*MiniSurround.delete()*
MiniSurround.find() mini-surround.txt /*MiniSurround.find()*
MiniSurround.gen_spec mini-surround.txt /*MiniSurround.gen_spec*
MiniSurround.gen_spec.input.treesitter() mini-surround.txt /*MiniSurround.gen_spec.input.treesitter()*
MiniSurround.highlight() mini-surround.txt /*MiniSurround.highlight()*
MiniSurround.replace() mini-surround.txt /*MiniSurround.replace()*
MiniSurround.setup() mini-surround.txt /*MiniSurround.setup()*
MiniSurround.update_n_lines() mini-surround.txt /*MiniSurround.update_n_lines()*
MiniSurround.user_input() mini-surround.txt /*MiniSurround.user_input()*
MiniTabline mini-tabline.txt /*MiniTabline*
MiniTabline.config mini-tabline.txt /*MiniTabline.config*
MiniTabline.default_format() mini-tabline.txt /*MiniTabline.default_format()*
MiniTabline.make_tabline_string() mini-tabline.txt /*MiniTabline.make_tabline_string()*
MiniTabline.setup() mini-tabline.txt /*MiniTabline.setup()*
MiniTest mini-test.txt /*MiniTest*
MiniTest-child-neovim mini-test.txt /*MiniTest-child-neovim*
MiniTest-child-neovim.get_screenshot() mini-test.txt /*MiniTest-child-neovim.get_screenshot()*
MiniTest-child-neovim.start() mini-test.txt /*MiniTest-child-neovim.start()*
MiniTest-child-neovim.type_keys() mini-test.txt /*MiniTest-child-neovim.type_keys()*
MiniTest-test-case mini-test.txt /*MiniTest-test-case*
MiniTest.add_note() mini-test.txt /*MiniTest.add_note()*
MiniTest.collect() mini-test.txt /*MiniTest.collect()*
MiniTest.config mini-test.txt /*MiniTest.config*
MiniTest.current mini-test.txt /*MiniTest.current*
MiniTest.execute() mini-test.txt /*MiniTest.execute()*
MiniTest.expect mini-test.txt /*MiniTest.expect*
MiniTest.expect.equality() mini-test.txt /*MiniTest.expect.equality()*
MiniTest.expect.error() mini-test.txt /*MiniTest.expect.error()*
MiniTest.expect.no_equality() mini-test.txt /*MiniTest.expect.no_equality()*
MiniTest.expect.no_error() mini-test.txt /*MiniTest.expect.no_error()*
MiniTest.expect.reference_screenshot() mini-test.txt /*MiniTest.expect.reference_screenshot()*
MiniTest.finally() mini-test.txt /*MiniTest.finally()*
MiniTest.gen_reporter mini-test.txt /*MiniTest.gen_reporter*
MiniTest.gen_reporter.buffer() mini-test.txt /*MiniTest.gen_reporter.buffer()*
MiniTest.gen_reporter.stdout() mini-test.txt /*MiniTest.gen_reporter.stdout()*
MiniTest.is_executing() mini-test.txt /*MiniTest.is_executing()*
MiniTest.new_child_neovim() mini-test.txt /*MiniTest.new_child_neovim()*
MiniTest.new_expectation() mini-test.txt /*MiniTest.new_expectation()*
MiniTest.new_set() mini-test.txt /*MiniTest.new_set()*
MiniTest.run() mini-test.txt /*MiniTest.run()*
MiniTest.run_at_location() mini-test.txt /*MiniTest.run_at_location()*
MiniTest.run_file() mini-test.txt /*MiniTest.run_file()*
MiniTest.setup() mini-test.txt /*MiniTest.setup()*
MiniTest.skip() mini-test.txt /*MiniTest.skip()*
MiniTest.stop() mini-test.txt /*MiniTest.stop()*
MiniTrailspace mini-trailspace.txt /*MiniTrailspace*
MiniTrailspace.config mini-trailspace.txt /*MiniTrailspace.config*
MiniTrailspace.highlight() mini-trailspace.txt /*MiniTrailspace.highlight()*
MiniTrailspace.setup() mini-trailspace.txt /*MiniTrailspace.setup()*
MiniTrailspace.trim() mini-trailspace.txt /*MiniTrailspace.trim()*
MiniTrailspace.trim_last_lines() mini-trailspace.txt /*MiniTrailspace.trim_last_lines()*
MiniTrailspace.unhighlight() mini-trailspace.txt /*MiniTrailspace.unhighlight()*
MiniVisits mini-visits.txt /*MiniVisits*
MiniVisits-examples mini-visits.txt /*MiniVisits-examples*
MiniVisits-index-specification mini-visits.txt /*MiniVisits-index-specification*
MiniVisits-overview mini-visits.txt /*MiniVisits-overview*
MiniVisits.add_label() mini-visits.txt /*MiniVisits.add_label()*
MiniVisits.add_path() mini-visits.txt /*MiniVisits.add_path()*
MiniVisits.config mini-visits.txt /*MiniVisits.config*
MiniVisits.config.list mini-visits.txt /*MiniVisits.config.list*
MiniVisits.gen_filter mini-visits.txt /*MiniVisits.gen_filter*
MiniVisits.gen_filter.default() mini-visits.txt /*MiniVisits.gen_filter.default()*
MiniVisits.gen_filter.this_session() mini-visits.txt /*MiniVisits.gen_filter.this_session()*
MiniVisits.gen_normalize mini-visits.txt /*MiniVisits.gen_normalize*
MiniVisits.gen_normalize.default() mini-visits.txt /*MiniVisits.gen_normalize.default()*
MiniVisits.gen_sort mini-visits.txt /*MiniVisits.gen_sort*
MiniVisits.gen_sort.default() mini-visits.txt /*MiniVisits.gen_sort.default()*
MiniVisits.gen_sort.z() mini-visits.txt /*MiniVisits.gen_sort.z()*
MiniVisits.get_index() mini-visits.txt /*MiniVisits.get_index()*
MiniVisits.iterate_paths() mini-visits.txt /*MiniVisits.iterate_paths()*
MiniVisits.list_labels() mini-visits.txt /*MiniVisits.list_labels()*
MiniVisits.list_paths() mini-visits.txt /*MiniVisits.list_paths()*
MiniVisits.normalize_index() mini-visits.txt /*MiniVisits.normalize_index()*
MiniVisits.read_index() mini-visits.txt /*MiniVisits.read_index()*
MiniVisits.register_visit() mini-visits.txt /*MiniVisits.register_visit()*
MiniVisits.remove_label() mini-visits.txt /*MiniVisits.remove_label()*
MiniVisits.remove_path() mini-visits.txt /*MiniVisits.remove_path()*
MiniVisits.rename_in_index() mini-visits.txt /*MiniVisits.rename_in_index()*
MiniVisits.reset_index() mini-visits.txt /*MiniVisits.reset_index()*
MiniVisits.select_label() mini-visits.txt /*MiniVisits.select_label()*
MiniVisits.select_path() mini-visits.txt /*MiniVisits.select_path()*
MiniVisits.set_index() mini-visits.txt /*MiniVisits.set_index()*
MiniVisits.setup() mini-visits.txt /*MiniVisits.setup()*
MiniVisits.write_index() mini-visits.txt /*MiniVisits.write_index()*
before mini-cursorword.txt /*before*
mini-base16-color-schemes mini-base16.txt /*mini-base16-color-schemes*
mini.ai mini-ai.txt /*mini.ai*
mini.align mini-align.txt /*mini.align*
mini.animate mini-animate.txt /*mini.animate*
mini.base16 mini-base16.txt /*mini.base16*
mini.basics mini-basics.txt /*mini.basics*
mini.bracketed mini-bracketed.txt /*mini.bracketed*
mini.bufremove mini-bufremove.txt /*mini.bufremove*
mini.clue mini-clue.txt /*mini.clue*
mini.colors mini-colors.txt /*mini.colors*
mini.comment mini-comment.txt /*mini.comment*
mini.completion mini-completion.txt /*mini.completion*
mini.cursorword mini-cursorword.txt /*mini.cursorword*
mini.deps mini-deps.txt /*mini.deps*
mini.diff mini-diff.txt /*mini.diff*
mini.doc mini-doc.txt /*mini.doc*
mini.extra mini-extra.txt /*mini.extra*
mini.files mini-files.txt /*mini.files*
mini.fuzzy mini-fuzzy.txt /*mini.fuzzy*
mini.git mini-git.txt /*mini.git*
mini.hipatterns mini-hipatterns.txt /*mini.hipatterns*
mini.hues mini-hues.txt /*mini.hues*
mini.icons mini-icons.txt /*mini.icons*
mini.indentscope mini-indentscope.txt /*mini.indentscope*
mini.jump mini-jump.txt /*mini.jump*
mini.jump2d mini-jump2d.txt /*mini.jump2d*
mini.map mini-map.txt /*mini.map*
mini.map-usage mini-map.txt /*mini.map-usage*
mini.misc mini-misc.txt /*mini.misc*
mini.move mini-move.txt /*mini.move*
mini.notify mini-notify.txt /*mini.notify*
mini.nvim mini.txt /*mini.nvim*
mini.nvim-buffer-local-config mini.txt /*mini.nvim-buffer-local-config*
mini.nvim-disabling-recipes mini.txt /*mini.nvim-disabling-recipes*
mini.operators mini-operators.txt /*mini.operators*
mini.pairs mini-pairs.txt /*mini.pairs*
mini.pick mini-pick.txt /*mini.pick*
mini.sessions mini-sessions.txt /*mini.sessions*
mini.splitjoin mini-splitjoin.txt /*mini.splitjoin*
mini.starter mini-starter.txt /*mini.starter*
mini.statusline mini-statusline.txt /*mini.statusline*
mini.surround mini-surround.txt /*mini.surround*
mini.tabline mini-tabline.txt /*mini.tabline*
mini.test mini-test.txt /*mini.test*
mini.trailspace mini-trailspace.txt /*mini.trailspace*
mini.visits mini-visits.txt /*mini.visits*
minicyan mini-base16.txt /*minicyan*
minischeme mini-base16.txt /*minischeme*
randomhue mini-hues.txt /*randomhue*
very mini-jump2d.txt /*very*

Binary file not shown.

After

Width:  |  Height:  |  Size: 621 B

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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

File diff suppressed because it is too large Load Diff

View File

@ -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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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

File diff suppressed because it is too large Load Diff

View File

@ -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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More