*mini.completion* Completion and signature help MIT License Copyright (c) 2021 Evgeni Chasnovski ------------------------------------------------------------------------------ *MiniCompletion* 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. |popupmenu-completion| is used to show completion suggestions. 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 directly starting with completed word are kept and are sorted according to LSP specification. Supports `additionalTextEdits`, like auto-import and others (see `# Notes`), and snippet items (best results require |mini.snippets| dependency). - 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). Signature help is shown if character to cursor's left is a dedicated trigger character (configured in `signatureHelpProvider.triggerCharacters` of LSP server capabilities) and updated without delay if is currently opened. Already shown window for signature help is fixed and is closed when there is nothing to show, its text is different, or when leaving Insert mode. Scroll in either info/signature window with `` / `` (by default). - 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) - Force two-stage/fallback completion (`` / `` by default). - Customizable highlighting of LSP items. Requires Neovim>=0.11. Use `config.lsp_completion.process_items` to set dedicated highlight group in supported fields: - - item label (`abbr` in terms of |complete-items|). By default only checks if item is marked as deprecated and sets `MiniCompletionDeprecated` highlight group. - - LSP kind ("Function", "Keyword", etc.). By default uses "lsp" category of |mini.icons| (if enabled). What it doesn't do: - Many configurable sources. - Automatic mapping of ``, ``, etc. Those tend to have highly variable user expectations. See `# Helpful mappings` for suggestions or use |MiniKeymap.map_multistep()| with `"pmenu_*"` built-in steps. # Dependencies ~ Suggested dependencies (provide extra functionality, will work without them): - Enabled |mini.icons| module to highlight LSP kind (requires Neovim>=0.11). If absent, |MiniCompletion.default_process_items()| does not add highlighting. Also take a look at |MiniIcons.tweak_lsp_kind()|. - Enabled |mini.snippets| module for better snippet handling (much recommended). If absent and custom snippet insert is not configured, falls back to |vim.snippet.expand()|. See |MiniCompletion.default_snippet_insert()|. # 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. # Suggested option values ~ Some options are set automatically (if not set before |MiniCompletion.setup()|): - |'completeopt'| is set to "menuone,noselect" for less intrusive popup. To enable fuzzy matching, manually set to "menuone,noselect,fuzzy". Consider also adding "nosort" flag to preserve initial order when filtering. - |'shortmess'| is appended with "c" flag for silent fallback. - |'complete'| gets removed "t" flag (if fallback action is default), as it leads to visible lags. # Snippets ~ As per LSP specification, some completion items can be supplied in the form of snippet - a template with both pre-defined text and places (called "tabstops") for user to interactively change/add text during snippet session. In |mini.completion| items that will insert snippet have "S" symbol shown in the popup (as part of `menu` in |complete-items|). To actually insert a snippet: - Select an item via / . This will insert item's label (usually not full snippet) first to reduce visual flicker. The full snippet text will be shown in info window if LSP server doesn't provide its own info for an item. - Press (|complete_CTRL-Y|) or attempt inserting a non-keyword character (like ; new character will be removed). It will clear text from previous step, set cursor, and call `lsp_completion.snippet_insert` with snippet text. - Press (|complete_CTRL-E|) to cancel snippet insert and properly end completion. See |MiniCompletion.default_snippet_insert()| for overview of how to work with inserted snippets. Notes: - To stop LSP server from suggesting snippets, disable (set to `false`) the following capability during LSP server start: `textDocument.completion.completionItem.snippetSupport`. - If snippet body doesn't contain tabstop, variable, tab, or newline, `lsp_completion.snippet_insert` is not called and text is inserted as-is. # Notes ~ - A 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 |MiniCompletion.setup()| config: >lua lsp_completion = { source_func = 'omnifunc', auto_setup = false } < - Set |'omnifunc'| option to exactly `v:lua.MiniCompletion.completefunc_lsp` for every client attach in an |LspAttach| event. Like this: >lua local on_attach = function(args) vim.bo[args.buf].omnifunc = 'v:lua.MiniCompletion.completefunc_lsp' end vim.api.nvim_create_autocmd('LspAttach', { callback = on_attach }) < This setup is not default to allow simultaneous usage of filetype-specific |'omnifunc'| (with manual |i_CTRL-X_CTRL-O|) and automated LSP completion. - Use |MiniCompletion.get_lsp_capabilities()| to get/set information about part of LSP specification supported by module. See its help for usability notes. - 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()|, disable |mini.completion| for input buffer (usually based on its |'filetype'|). # Comparisons ~ - [hrsh7th/nvim-cmp](https://github.com/hrsh7th/nvim-cmp): - Implements own popup menu to show completion candidates, while this module reuses |ins-completion-menu|. - Has more complex design which allows multiple sources, each in a form of a separate plugin. This module has two built-in: LSP and fallback. - Requires separate plugin for automated signature help. - Implements own "ghost text" feature, while this module does not. - [Saghen/blink.cmp](https://github.com/Saghen/blink.cmp): - Mostly similar to `nvim-cmp` comparison: provides more features at the cost of more code and config complexity, while this module is designed to provide only a handful of "enough" features while relying on Neovim's built-in capabilities as much as possible. - Both provide automated signature help out of the box. # Helpful mappings ~ If there is |mini.keymap| available, prefer using |MiniKeymap.map_multistep()| with `"pmenu_*"` built-in steps. See |MiniKeymap-examples| for examples. To use `` and `` 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('', [[pumvisible() ? "\" : "\"]]) imap_expr('', [[pumvisible() ? "\" : "\"]]) < To get more consistent behavior of ``, you can use this template in your `init.lua` to make customized mapping: >lua _G.cr_action = function() -- If there is selected item in popup, accept it with if vim.fn.complete_info()['selected'] ~= -1 then return '\25' end -- Fall back to plain ``. You might want to customize according -- to other plugins. For example if 'mini.pairs' is set up, replace -- next line with `return MiniPairs.cr()` return '\r' end vim.keymap.set('i', '', 'v:lua.cr_action()', { expr = true }) < # Highlight groups ~ *MiniCompletion-hl-groups* - `MiniCompletionActiveParameter` - signature active parameter. - `MiniCompletionDeprecated` - candidates that marked as deprecated. - `MiniCompletionInfoBorderOutdated` - info window border when text is outdated due to explicit delay during fast movement through candidates. To change any highlight group, set it directly with |nvim_set_hl()|. # 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-events* To allow user customization, certain |User| autocommand events are triggered under common circumstances: - Info and signature help window: - `MiniCompletionWindowOpen` - after opening new window. - `MiniCompletionWindowUpdate` - after updating existing window. Each event's |event-data| table contains `kind` (one of "info" or "signature") and `win_id` (affected window identifier) fields. ------------------------------------------------------------------------------ *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` Defaults ~ >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()`; default "single"). window = { info = { height = 25, width = 80, border = nil }, signature = { height = 25, width = 80, border = nil }, }, -- 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, -- A function which takes LSP 'textDocument/completion' response items -- (each with `client_id` field for item's server) and word to complete. -- Output should be a table of the same nature as input. Common use case -- is custom filter/sort. Default: `default_process_items` process_items = nil, -- A function which takes a snippet as string and inserts it at cursor. -- Default: `default_snippet_insert` which tries to use 'mini.snippets' -- and falls back to `vim.snippet.expand`. snippet_insert = nil, }, -- Fallback action as function/string. Executed in Insert mode. -- To use built-in completion (`:h ins-completion`), set its mapping as -- string. Example: set '' for 'whole lines' completion. fallback_action = '', -- Module mappings. Use `''` (empty string) to disable one. Some of them -- might conflict with system mappings. mappings = { -- Force two-step/fallback completions force_twostep = '', force_fallback = '', -- Scroll info/signature window down/up. When overriding, check for -- conflicts with built-in keys for popup menu (like ``/`` -- for 'completefunc'/'omnifunc' source function; or ``/``). scroll_down = '', scroll_up = '', }, } < ------------------------------------------------------------------------------ *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.scroll()* `MiniCompletion.scroll`({direction}) Scroll in info/signature window Designed to be used in |:map-|. Scrolling is done as if |CTRL-F| and |CTRL-B| is pressed inside target window. Used in default `config.mappings.scroll_xxx` mappings. Parameters ~ {direction} `(string)` One of `"down"` or `"up"`. Return ~ `(boolean)` Whether scroll is scheduled to be done. ------------------------------------------------------------------------------ *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'` strings. Default: array containing all of them. ------------------------------------------------------------------------------ *MiniCompletion.completefunc_lsp()* `MiniCompletion.completefunc_lsp`({findstart}, {base}) Module's |complete-functions| 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}, {opts}) Default processing of LSP items Steps: - Filter and sort items according to supplied method. - Arrange items further by completion item kind according to their priority. - Add `MiniCompletionDeprecated` if item is marked as deprecated. - If |mini.icons| is enabled, add based on the "lsp" category. Example of forcing fuzzy matching, filtering out `Text` items, and putting `Snippet` items last: >lua local kind_priority = { Text = -1, Snippet = 99 } local opts = { filtersort = 'fuzzy', kind_priority = kind_priority } local process_items = function(items, base) return MiniCompletion.default_process_items(items, base, opts) end require('mini.completion').setup({ lsp_completion = { process_items = process_items }, }) < Parameters ~ {items} `(table)` Array of items from LSP response. {base} `(string)` Base for which completion is done. See |complete-functions|. {opts} `(table|nil)` Options. Possible fields: - `(string|function)` - method of filtering and sorting items. If string, should be one of the following: - `'prefix'` - filter out items not starting with `base`, sort according to LSP specification. Use `filterText` and `sortText` respectively with fallback to `label`. - `'fuzzy'` - filter and sort with |matchfuzzy()| using `filterText`. - `'none'` - no filter and no sort. If callable, should take `items` and `base` arguments and return items array. Default: `'fuzzy'` if |'completeopt'| contains "fuzzy", `'prefix'` otherwise. - `(table)` - map of completion item kinds (like `Variable`, `Snippet`; see string keys of `vim.lsp.protocol.CompletionItemKind`) to their numerical priority. It will be used after applying to arrange by completion item kind: items with negative priority kinds will be filtered out, the rest are sorted by decreasing priority (preserving order in case of same priority). Priorities can be any number, only matters how they compare to each other. Value 100 is used for missing kinds (i.e. not all can be supplied). Default: `{}` (all equal priority). Return ~ `(table)` Array of processed items from LSP response. ------------------------------------------------------------------------------ *MiniCompletion.default_snippet_insert()* `MiniCompletion.default_snippet_insert`({snippet}) Default snippet insert Order of preference: - Use |mini.snippets| if set up (i.e. after `require('mini.snippets').setup()`). - Fall back to |vim.snippet.expand()|. After snippet is inserted, user is expected to navigate/jump between dedicated places (tabstops) to adjust inserted text as needed: - |mini.snippets| by default uses / to jump to next/previous tabstop. Can be adjusted in `mappings` of |MiniSnippets.config|. - |vim.snippet| on Neovim=0.10 requires manually created mappings for jumping between tabstops (see |vim.snippet.jump()|). Neovim>=0.11 sets them up automatically to / (if not overridden by user). End session by navigating all the way to the last tabstop. In |mini.snippets|: - Also make any text edit or exit Insert mode to end the session. This allows smoother navigation to previous tabstops in case of a lately spotted typo. - Press `` to force session stop. Parameters ~ {snippet} `(string)` Snippet body to insert at cursor. See also ~ - |MiniSnippets-session| if |mini.snippets| is set up. - |vim.snippet| for Neovim's built-in snippet engine. ------------------------------------------------------------------------------ *MiniCompletion.get_lsp_capabilities()* `MiniCompletion.get_lsp_capabilities`({opts}) Get client LSP capabilities Possible usages: - On Neovim>=0.11 via |vim.lsp.config()|: >lua vim.lsp.config('*', {capabilities = MiniCompletion.get_lsp_capabilities()}) < - Together with |vim.lsp.protocol.make_client_capabilities()| to get the full client capabilities (use |vim.tbl_deep_extend()| to merge tables). - Manually execute `:=MiniCompletion.get_lsp_capabilities()` to see the info. Notes: - It declares completion resolve support for `'additionalTextEdits'` (usually used for something like auto-import feature), as it is usually a more robust choice for various LSP servers. As a consequence, this requires selecting completion item and waiting for `config.delay.info` milliseconds plus server response time (i.e. until information window shows relevant text). To not have to wait after an item selection and if the server handles absent `'additionalTextEdits'` well, set `opts.resolve_additional_text_edits = false`. Parameters ~ {opts} `(table|nil)` Options. Possible fields: - `(boolean)` - whether to declare `'additionalTextEdits'` as possible to resolve in `'completionitem/resolve'` request. See above "Notes" section. Default: `true`. Return ~ `(table)` Data about LSP capabilities supported by |mini.completion|. Has same structure as relevant parts of |vim.lsp.protocol.make_client_capabilities()|. See also ~ Structures of `completionClientCapabilities` and `signatureHelpClientCapabilities` at https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification vim:tw=78:ts=8:noet:ft=help:norl: