mini.completion documentation

Generated from the main branch of ‘mini.nvim’

mini.completion Completion and signature help

MIT License Copyright (c) 2021 Evgeni Chasnovski


Module

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 <C-f> / <C-b> (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 (<C-Space> / <A-Space> 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:

    • <abbr_hlgroup> - item label (abbr in terms of complete-items). By default only checks if item is marked as deprecated and sets MiniCompletionDeprecated highlight group.

    • <kind_hlgroup> - 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 <CR>, <Tab>, 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):

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 <C-n> 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 <C-n> / <C-p>. 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 <C-y> (complete_CTRL-Y) or attempt inserting a non-keyword character (like <CR>; new character will be removed). It will clear text from previous step, set cursor, and call lsp_completion.snippet_insert with snippet text.

  • Press <C-e> (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 tabstops, 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:

      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:

      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:

    • 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:

    • 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 <Tab> and <S-Tab> for navigation through completion list, make these mappings:

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:

_G.cr_action = function()
  -- If there is selected item in popup, accept it with <C-y>
  if vim.fn.complete_info()['selected'] ~= -1 then return '\25' end
  -- Fall back to plain `<CR>`. 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', '<CR>', 'v:lua.cr_action()', { expr = true })

Highlight 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.


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.


setup()

MiniCompletion.setup({config})

Module setup

Parameters

{config} (table|nil) Module config table. See MiniCompletion.config.

Usage

require('mini.completion').setup() -- use default config
-- OR
require('mini.completion').setup({}) -- replace {} with your config table

config

MiniCompletion.config

Defaults

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` (on Neovim>=0.10).
    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 '<C-x><C-l>' for 'whole lines' completion.
  fallback_action = '<C-n>',

  -- Module mappings. Use `''` (empty string) to disable one. Some of them
  -- might conflict with system mappings.
  mappings = {
    -- Force two-step/fallback completions
    force_twostep = '<C-Space>',
    force_fallback = '<A-Space>',

    -- Scroll info/signature window down/up. When overriding, check for
    -- conflicts with built-in keys for popup menu (like `<C-u>`/`<C-o>`
    -- for 'completefunc'/'omnifunc' source function; or `<C-n>`/`<C-p>`).
    scroll_down = '<C-f>',
    scroll_up = '<C-b>',
  },
}

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.


complete_fallback()

MiniCompletion.complete_fallback()

Run fallback completion


scroll()

MiniCompletion.scroll({direction})

Scroll in info/signature window

Designed to be used in :map-<expr>. 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.


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.


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().


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 <abbr_hlgroup> if item is marked as deprecated.

  • If mini.icons is enabled, add <kind_hlgroup> based on the “lsp” category.

Example of forcing fuzzy matching, filtering out Text items, and putting Snippet items last:

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:

  • <filtersort> (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.

  • <kind_priority> (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 <filtersort> 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.


default_snippet_insert()

MiniCompletion.default_snippet_insert({snippet})

Default snippet insert

Order of preference:

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 <C-l> / <C-h> 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 <Tab> / <S-Tab> (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 <C-c> to force session stop.

Parameters

{snippet} (string) Snippet body to insert at cursor.

See also


get_lsp_capabilities()

MiniCompletion.get_lsp_capabilities({opts})

Get client LSP capabilities

Possible usages:

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:

  • <resolve_additional_text_edits> (boolean) - whether to declare

    'additionalTextEdits' as possible to resolve in 'completionitem/resolve'

    requrest. 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