*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 `<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):

- 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, |vim.snippet.expand()|
  is used on Neovim>=0.10 (nothing extra is done on earlier versions).
  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 <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 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 `<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

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

------------------------------------------------------------------------------
                                                         *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` (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>',
    },
  }
<
------------------------------------------------------------------------------
                                            *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-<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.

------------------------------------------------------------------------------
                                                         *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-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` <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: >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:
  - <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.

------------------------------------------------------------------------------
                                       *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()`).
- Use |vim.snippet.expand()| on Neovim>=0.10
- Add snippet text at cursor as is.

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


 vim:tw=78:ts=8:noet:ft=help:norl: