8000 mini.nvim/lua/mini/pick.lua at main · PeterCardenas/mini.nvim · GitHub
[go: up one dir, main page]

Skip to content

Latest commit

 

History

History
3695 lines (3271 loc) · 147 KB

pick.lua

File metadata and controls

3695 lines (3271 loc) · 147 KB
--- *mini.pick* Pick anything
---
--- MIT License Copyright (c) 2023 Evgeni Chasnovski
--- Features:
---
--- - Single window general purpose interface for picking element from any array.
---
--- - On demand toggleable preview and info views.
---
--- - Interactive query matching (filter+sort) with fast non-blocking default
--- which does fuzzy matching and allows other modes (|MiniPick.default_match()|).
---
--- - Built-in pickers (see |MiniPick.builtin|):
--- - Files.
--- - Pattern match (for fixed pattern or with live feedback; both allow
--- file filtering via glob patterns).
--- - Buffers.
--- - Help tags.
--- - CLI output.
--- - Resume latest picker.
---
--- - |:Pick| command to work with extensible |MiniPick.registry|.
---
--- - |vim.ui.select()| implementation. To adjust, use |MiniPick.ui_select()|
--- or save-restore `vim.ui.select` manually after calling |MiniPick.setup()|.
---
--- - Rich and customizable built-in |MiniPick-actions| when picker is active:
--- - Manually change currently focused item.
--- - Scroll vertically and horizontally.
--- - Toggle preview or info view.
--- - Mark/unmark items to choose later.
--- - Refine current matches (make them part of a new picker).
--- - And many more.
---
--- - Minimal yet flexible |MiniPick-source| specification with:
--- - Items (array, callable, or manually set later).
--- - Source name.
--- - Working directory.
--- - Matching algorithm.
--- - Way matches are shown in main window.
--- - Item preview.
--- - "On choice" action for current and marked items.
---
--- - Custom actions/keys can be configured globally, per buffer, or per picker.
---
--- - Out of the box support for 'ignorecase' and 'smartcase'.
---
--- - Match caching to increase responsiveness on repeated prompts.
---
--- Notes:
--- - Works on all supported versions but Neovim>=0.10 will give more visual
--- feedback in floating window footer.
---
--- - For more pickers see |MiniExtra.pickers|.
---
--- Sources with more details:
--- - |MiniPick-overview|
--- - |MiniPick-source|
--- - |MiniPick-actions|
--- - |MiniPick-examples|
--- - |MiniPick.builtin|
---
--- # Dependencies ~
---
--- Suggested dependencies (provide extra functionality, will work without them):
---
--- - Enabled |mini.icons| module to show icons near the items for actual paths.
--- Falls back to 'nvim-tree/nvim-web-devicons' plugin or no icons will be used.
---
--- - *MiniPick-cli-tools* CLI tool(s) to power |MiniPick.builtin.files()|,
--- |MiniPick.builtin.grep()|, and |MiniPick.builtin.grep_live()| built-in pickers:
--- - `rg` (github.com/BurntSushi/ripgrep; enough for all three; recommended).
--- - `fd` (github.com/sharkdp/fd; for `files` only).
--- - `git` (github.com/git/git; enough for all three).
---
--- Note: CLI tools are called only with basic arguments needed to get items.
--- To customize the output, use their respective configuration approaches.
--- Here are some examples of where to start:
--- - github.com/BurntSushi/ripgrep/blob/master/GUIDE.md#configuration-file
--- - github.com/sharkdp/fd#excluding-specific-files-or-directories
--- - git-scm.com/docs/gitignore
---
--- # Setup ~
---
--- This module needs a setup with `require('mini.pick').setup({})` (replace
--- `{}` with your `config` table). It will create global Lua table `MiniPick`
--- which you can use for scripting or manually (with `:lua MiniPick.*`).
---
--- See |MiniPick.config| for available config settings.
---
--- You can override runtime config settings locally to buffer inside
--- `vim.b.minipick_config` which should have same structure as `MiniPick.config`.
--- See |mini.nvim-buffer-local-config| for more details.
---
--- # Comparisons ~
---
--- - [nvim-telescope/telescope.nvim](https://github.com/nvim-telescope/telescope.nvim):
--- - The main inspiration for this module, so there is significant overlap.
--- - Has three (or two) window UI (prompt, matches, preview), while this
--- module combines everything in one window. It allows more straightforward
--- customization for unusual scenarios.
--- - Default match algorithm is somewhat slow, while this module should
--- match relatively lag-free for at least 100K+ items.
--- - Has many built-in pickers, while this module has handful at its core
--- relying on other 'mini.nvim' modules to provide more (see |mini.extra|).
---
--- - [ibhagwan/fzf-lua](https://github.com/ibhagwan/fzf-lua):
--- - Mostly same comparison as with 'nvim-telescope/telescope.nvim'.
--- - Requires [junegunn/fzf](https://github.com/junegunn/fzf) installed to
--- power fuzzy matching, while this module provides built-in Lua matching.
---
--- # Highlight groups ~
---
--- - `MiniPickBorder` - window border.
--- - `MiniPickBorderBusy` - window border while picker is busy processing.
--- - `MiniPickBorderText` - non-prompt on border.
--- - `MiniPickCursor` - cursor during active picker (hidden by default).
--- - `MiniPickIconDirectory` - default icon for directory.
--- - `MiniPickIconFile` - default icon for file.
--- - `MiniPickHeader` - headers in info buffer and previews.
--- - `MiniPickMatchCurrent` - current matched item.
--- - `MiniPickMatchMarked` - marked matched items.
--- - `MiniPickMatchRanges` - ranges matching query elements.
--- - `MiniPickNormal` - basic foreground/background highlighting.
--- - `MiniPickPreviewLine` - target line in preview.
--- - `MiniPickPreviewRegion` - target region in preview.
--- - `MiniPickPrompt` - prompt.
--- - `MiniPickPromptCaret` - caret in prompt.
--- - `MiniPickPromptPrefix` - prefix of the prompt.
---
--- To change any highlight group, set it directly with |nvim_set_hl()|.
---@tag MiniPick
--- To allow user customization and integration of external tools, certain |User|
--- autocommand events are triggered under common circumstances:
---
--- - `MiniPickMatch` - just after updating query matches or setting items.
--- - `MiniPickStart` - just after picker has started.
--- - `MiniPickStop` - just before picker is stopped.
---@tag MiniPick-events
--- General idea is to take array of objects, display them with interactive
--- filter/sort/navigate/preview, and allow to choose one or more items.
---
--- # How to start a picker ~
---
--- - Use |MiniPick.start()| with `opts.source` defining |MiniPick-source|.
--- Example: `MiniPick.start({ source = { items = vim.fn.readdir('.') } })`
---
--- - Use any of |MiniPick.builtin| pickers directly.
--- Example: `MiniPick.builtin.files({ tool = 'git' })`
---
--- - Use |:Pick| command which uses customizable pickers from |MiniPick.registry|.
--- Example: `:Pick files tool='git'`
---
--- # User interface ~
---
--- UI consists from a single window capable of displaying three different views:
--- - "Main" - where current query matches are shown.
--- - "Preview" - preview of current item (toggle with `<Tab>`).
--- - "Info" - general info about picker and its state (toggle with `<S-Tab>`).
---
--- Current prompt is displayed at the top left of the window border with vertical
--- line indicating caret (current input position).
---
--- Bottom part of window border displays (in Neovim>=0.10) extra visual feedback:
--- - Left part is a picker name.
--- - Right part contains information in the format >
---
--- <current index in matches> | <match count> | <marked count> / <total count>
--- <
--- When picker is busy (like if there are no items yet set or matching is active)
--- window border changes color to be `MiniPickBorderBusy` after `config.delay.busy`
--- milliseconds of idle time.
---
--- # Life cycle ~
---
--- - Type characters to filter and sort matches. It uses |MiniPick.default_match()|
--- with `query` being an array of pressed characters.
--- Overview of how it matches:
--- - If query starts with `'`, the match is exact.
--- - If query starts with `^`, the match is exact at start.
--- - If query ends with `$`, the match is exact at end.
--- - If query starts with `*`, the match is forced to be fuzzy.
--- - Otherwise match is fuzzy.
--- - Sorting is done to first minimize match width and then match start.
--- Nothing more: no favoring certain places in string, etc.
---
--- - Type special keys to perform |MiniPick-actions|. Here are some basic ones:
--- - `<C-n>` / `<Down>` moves down; `<C-p>` / `<Up>` moves up.
--- - `<Left>` / `<Right>` moves prompt caret left / right.
--- - `<S-Tab>` toggles information window with all available mappings.
--- - `<Tab>` toggles preview.
--- - `<C-x>` / `<C-a>` toggles current / all item(s) as (un)marked.
--- - `<C-Space>` / `<M-Space>` makes all matches or marked items as new picker.
--- - `<CR>` / `<M-CR>` chooses current/marked item(s).
--- - `<Esc>` / `<C-c>` stops picker.
---
--- # Implementation details ~
---
--- - Processing key typing is done via a dedicated key query process for more
--- control over their side effects. As a result, regular mappings don't work
--- here and picker's window needs to be current as long as it is shown.
--- Changing window focus leads to automatic picker stop (after small delay).
--- - Any picker is non-blocking but waits to return the chosen item. Example:
--- `file = MiniPick.builtin.files()` allows other actions to be executed when
--- picker is shown while still assigning `file` with value of the chosen item.
---@tag MiniPick-overview
--- Source is defined as a `source` field inside one of (in increasing priority):
--- - |MiniPick.config| - has global effect.
--- - `vim.b.minipick_config` - has buffer-local effect.
--- - `opts.source` in picker call - has effect for that particular call.
---
--- Example of source to choose from |arglist|: >lua
---
--- { items = vim.fn.argv, name = 'Arglist' }
--- <
--- Note: this is mostly useful for writing pickers. Can safely skip if you
--- want to just use provided pickers.
---
--- # Items ~
--- *MiniPick-source.items*
---
--- `source.items` defines items to choose from. It should be one of the following:
--- - Array of objects which can have different types. Any type is allowed.
--- - `nil`. Picker waits for explicit |MiniPick.set_picker_items()| call.
--- - Callable returning any of the previous types. Will be called once on start
--- with source's `cwd` set as |current-directory|.
---
--- *MiniPick-source.items-stritems*
--- Matching is done for items array based on the string representation of its
--- elements (here called "stritems"). For single item it is computed as follows:
--- - Callable is called once with output used in next steps.
--- - String item is used as is.
--- - String <text> field of table item is used (if present).
--- - Use output of |vim.inspect()|.
---
--- Example: >lua
---
--- items = { 'aaa.txt', { text = 'bbb' }, function() return 'ccc' end }
--- -- corresponding stritems are { 'aaa.txt', 'bbb', 'ccc' }
--- <
--- Default value is `nil`, assuming it always be supplied by the caller.
---
--- *MiniPick-source.items-common*
--- There are so 10BC8 me recommendations for common item types in order for them to work
--- out of the box with |MiniPick.default_show()|, |MiniPick.default_preview()|,
--- |MiniPick.default_choose()|, |MiniPick.default_choose_marked()|:
---
--- - Path (file or directory). Use string or `path` field of a table. Path can
--- be either absolute, relative to the `source.cwd`, or have a general URI format
--- (only if supplied as table field).
--- Examples: `'aaa.txt'`, `{ path = 'aaa.txt' }`
---
--- - Buffer. Use buffer id as number, string, or `bufnr` / `buf_id` / `buf`
--- field of a table (any name is allowed).
--- Examples: `1`, `'1'`, `{ bufnr = 1 }`, `{ buf_id = 1 }`, `{ buf = 1 }`
---
--- - Line in file or buffer. Use table representation with `lnum` field with line
--- number (starting from 1) or string in "<path>\0<line>" format (`\0` is
--- an actual null character; don't escape the slash; may need to be `\000`).
--- Examples: >lua
---
--- { path = 'aaa.txt', lnum = 2 }, 'aaa.txt\0002', { bufnr = 1, lnum = 3 }
--- <
--- - Position in file or buffer. Use table representation with `lnum` and `col`
--- fields with line and column numbers (starting from 1) or string in
--- "<path>\0<line>\0<col>" format (`\0` is an actual null character, don't
--- escape the slash; may need to be `\000`).
--- Examples: >lua
---
--- { path = 'aaa.txt', lnum = 2, col = 3 }, 'aaa.txt\0' .. '2\0003',
--- { bufnr = 1, lnum = 3, col = 4 }
--- <
--- - Region in file or buffer. Use table representation with `lnum`, `col`,
--- `end_lnum`, `end_col` fields for start and end line/column. All numbers
--- start from 1, end line is inclusive, end column is exclusive.
--- This naming is similar to |getqflist()| and |diagnostic-structure|.
--- Examples: >lua
---
--- { path = 'aaa.txt', lnum = 2, col = 3, end_lnum = 4, end_col = 5 },
--- { bufnr = 1, lnum = 3, col = 4, end_lnum = 5, end_col = 6 }
--- <
--- Note: all table items will benefit from having `text` field for better matching.
---
--- # Name ~
--- *MiniPick-source.name*
---
--- `source.name` defines the name of the picker to be used for visual feedback.
---
--- Default value is "<No name>".
---
--- # Current working directory ~
--- *MiniPick-source.cwd*
---
--- `source.cwd` is a string defining the current working directory in which
--- picker operates. It should point to a valid actually present directory path.
--- This is a part of source to allow persistent way to use relative paths,
--- i.e. not depend on current directory being constant after picker start.
--- It also makes the |MiniPick.builtin.resume()| picker more robust.
---
--- It will be set as local |current-directory| (|:lcd|) of picker's main window
--- to allow simpler code for "in window" functions (choose/preview/custom/etc.).
---
--- Default value is |current-directory|.
---
--- # Match ~
--- *MiniPick-source.match*
---
--- `source.match` is a callable defining how stritems
--- (see |MiniPick-source.items-stritems|) are matched (filtered and sorted) based
--- on the query.
---
--- It will be called with the following arguments:
--- - `stritems` - all available stritems for current picker.
--- - `inds` - array of `stritems` indexes usually pointing at current matches.
--- It does point to current matches in the case of interactively appending
--- character at the end of the query. It assumes that matches for such bigger
--- query is a subset of previous matches (implementation can ignore it).
--- This can be utilized to increase performance by checking fewer stritems.
--- - `query` - array of strings. Usually (like is common case of user interactively
--- typing query) each string represents one character. However, any strings are
--- allowed, as query can be set with |MiniPick.set_picker_query()|.
---
--- It should either return array of match indexes for stritems elements matching
--- the query (synchronous) or explicitly use |MiniPick.set_picker_match_inds()|
--- to set them (may be asynchronous).
---
--- Notes:
--- - The result can be any array of `stritems` indexes, i.e. not necessarily
--- a subset of input `inds`.
---
--- - Both `stritems` and `query` depend on values of 'ignorecase' and 'smartcase'.
--- If query shows "ignore case" properties (only 'ignorecase' is set or both
--- 'ignorecase' / 'smartcase' are set and query has only lowercase characters),
--- then `stritems` and `query` will have only lowercase characters.
--- This allows automatic support for case insensitive matching while being
--- faster and having simpler match function implementation.
---
--- - Writing custom `source.match` usually means also changing |MiniPick-source.show|
--- because it is used to highlight stritems parts actually matching the query.
---
--- Example of simple "exact" `match()` preserving initial order: >lua
---
--- local match_exact = function(stritems, inds, query)
--- local prompt_pattern = vim.pesc(table.concat(query))
--- local f = function(i) return stritems[i]:find(prompt_pattern) ~= nil end
--- return vim.tbl_filter(f, inds)
--- end
--- -- For non-blocking version see `:h MiniPick.poke_is_picker_active()`
--- <
--- Default value is |MiniPick.default_match()|.
---
--- # Show ~
--- *MiniPick-source.show*
---
--- `source.show` is a callable defining how matched items are shown in the window.
---
--- It will be called with the following arguments:
--- - `buf_id` - identifier of the target buffer.
--- - `items_to_show` - array of actual items to be shown in `buf_id`. This is
--- a subset of currently matched items computed to fit in current window view.
--- - `query` - array of strings. Same as in `source.match`.
---
--- It should update buffer `buf_id` to visually represent `items_to_show`
--- __one item per line starting from line one__ (it shouldn't depend on
--- `options.content_from_bottom`). This also includes possible visualization
--- of which parts of stritem actually matched query.
---
--- Example (assuming string items; without highlighting): >lua
---
--- local show_prepend = function(buf_id, items_arr, query)
--- local lines = vim.tbl_map(function(x) return 'Item: ' .. x end, items_arr)
--- vim.api.nvim_buf_set_lines(buf_id, 0, -1, false, lines)
--- end
--- <
--- Default value is |MiniPick.default_show()|.
---
--- # Preview ~
--- *MiniPick-source.preview*
---
--- `source.preview` is a callable defining how item preview is done.
---
--- It will be called with the following arguments:
--- - `buf_id` - identifier of the target buffer. Note: for every separate instance
--- of item previewing new scratch buffer is be created.
--- - `item` - item to preview.
---
--- It should update buffer `buf_id` to visually represent `item`.
---
--- Example: >lua
---
--- local preview_inspect = function(buf_id, item)
--- local lines = vim.split(vim.inspect(item), '\n')
--- vim.api.nvim_buf_set_lines(buf_id, 0, -1, false, lines)
--- end
--- <
--- Default value is |MiniPick.default_preview()|.
---
--- # Choose an item ~
--- *MiniPick-source.choose*
---
--- `source.choose` is a callable defining what to do when an item is chosen.
---
--- It will be called with the following arguments:
--- - `item` - chosen item. Always non-`nil`.
---
--- It should perform any intended "choose" action for an item and return
--- a value indicating whether picker should continue (i.e. not stop):
--- `nil` and `false` will stop picker, other values will continue.
---
--- Notes:
--- - It is called when picker window is still current. Use `windows.target` value
--- from |MiniPick.get_picker_state()| output to do something with target window.
---
--- Example: >lua
---
--- local choose_file_continue = function(item)
--- if vim.fn.filereadable(item) == 0 then return end
--- vim.api.nvim_win_call(
--- MiniPick.get_picker_state().windows.main,
--- function() vim.cmd('edit ' .. item) end
--- )
--- return true
--- end
--- <
--- Default value is |MiniPick.default_choose()|.
---
--- # Choose marked items ~
--- *MiniPick-source.choose_marked*
---
--- `source.choose_marked` is a callable defining what to do when marked items
--- (see |MiniPick-actions-mark|) are chosen. Serves as a companion to
--- `source.choose` which can choose several items.
---
--- It will be called with the following arguments:
--- - `items_marked` - array of marked items. Can be empty.
---
--- It should perform any intended "choose" action for several items and return
--- a value indicating whether picker should continue (i.e. not stop):
--- `nil` and `false` will stop picker, other values will continue.
---
--- Notes:
--- - It is called when picker window is still current. Use `windows.target` value
--- from |MiniPick.get_picker_state()| output to do something with target window.
---
--- Example: >lua
---
--- local choose_marked_print = function(items) print(vim.inspect(items)) end
--- <
--- Default value is |MiniPick.default_choose_marked()|.
---@tag MiniPick-source
--- When picker is active, `mappings` table defines a set of special keys which when
--- pressed will execute certain actions. Those can be of two types:
--- - Built-in: actions present in default `config.mappings`. Can be only overridden
--- with a different key.
--- - Custom: user defined actions. Should be a table with `char` and `func` fields.
---
---
--- # Built-in ~
---
--- ## Caret ~
--- *MiniPick-actions-caret*
---
--- User can add character not only at query end, but more generally at caret.
---
--- - `mappings.caret_left` - move caret to left.
--- - `mappings.caret_right` - move caret to right.
---
--- ## Choose ~
--- *MiniPick-actions-choose*
---
--- Choose is a fundamental action that actually implements the intent of
--- calling a picker, i.e. pick an item.
---
--- - `mappings.choose` - choose as is, i.e. apply `source.choose` for current item.
--- - `mappings.choose_in_split` - make horizontal split at target window, update
--- target window to the new split, and choose.
--- - `mappings.choose_in_tabpage` - same as `choose_in_split`, but create tabpage.
--- - `mappings.choose_in_vsplit` - same as `choose_in_split`, but split vertically.
--- - `mappings.choose_marked` - choose marked items as is, i.e.
--- apply `source.choose_marked` at current marked items.
---
--- ## Delete ~
--- *MiniPick-actions-delete*
---
--- Delete actions are for deleting elements from query.
---
--- - `mappings.delete_char` - delete one character to the left.
--- - `mappings.delete_char_right` - delete one character to the right.
--- - `mappings.delete_left` - delete everything to the left (like |i_CTRL-U|).
--- - `mappings.delete_word` - delete word to the left (like |i_CTRL-W|).
---
--- ## Mark ~
--- *MiniPick-actions-mark*
---
--- Marking is an action of adding certain items to a separate list which then can
--- be chosen with `mappings.choose_marked` (for example, sent to quickfix list).
--- This is a companion to a regular choosing which can pick only one item.
---
--- - `mappings.mark` - toggle marked/unmarked state of current item.
--- - `mappings.mark_all` - toggle marked/unmarked state (mark all if 10BC8 not all
--- marked; unmark all otherwise) of all currently matched items.
---
--- Notes:
--- - Marks persist across queries and matches. For example, user can make a query
--- with marking all matches several times and marked items from all queries
--- will be preserved.
---
--- ## Move ~
--- *MiniPick-actions-move*
---
--- Move is a fundamental action of changing which item is current.
---
--- - `mappings.move_down` - change focus to the item below.
--- - `mappings.move_start` change focus to the first currently matched item
--- - `mappings.move_up` - change focus to the item above.
---
--- Notes:
--- - Up and down wrap around edges: `move_down` on last item moves to first,
--- `move_up` on first moves to last.
--- - Moving when preview or info view is shown updates the view with new item.
--- - These also work with non-overridable alternatives:
--- - `<Down>` moves down.
--- - `<Home>` moves to first matched.
--- - `<Up>` moves up.
---
--- ## Paste ~
--- *MiniPick-actions-paste*
---
--- Paste is an action to paste content of |registers| at caret.
---
--- - `mappings.paste` - paste from register defined by the next key press.
---
--- Notes:
--- - Does not support expression register `=`.
--- - Supports special cases of register: <C-f> (as |c_CTRL-R_CTRL-F|),
--- <C-w> (as |c_CTRL-R_CTRL-W|), <C-a> (as |c_CTRL-R_CTRL-A|),
--- <C-l> (as |c_CTRL-R_CTRL-L|).
---
--- ## Refine ~
--- *MiniPick-actions-refine*
---
--- Refine is an action that primarily executes the following:
--- - Takes certain items and makes them be all items (in order they are present).
--- - Resets query.
--- - Updates `source.match` to be the one from config.
---
--- - `mappings.refine` - refine currently matched items.
--- - `mappings.refine_marked` - refine currently marked items.
---
--- This action is useful in at least two cases:
--- - Perform consecutive "narrowing" queries. Example: to get items that contain
--- both "hello" and "world" exact matches (in no particular order) with default
--- matching, type "'hello" (notice "'" at the start) followed by `<C-Space>` and
--- another "'world".
--- - Reset `match` to default. Particularly useful in |MiniPick.builtin.grep_live()|.
---
--- ## Scroll ~
--- *MiniPick-actions-scroll*
---
--- Scroll is an action to either move current item focus further than to the
--- neighbor item or adjust window view to see more information.
---
--- - `mappings.scroll_down` - when matches are shown, go down by the amount of
--- visible matches. In preview and info view - scroll down as with |CTRL-F|.
--- - `mappings.scroll_left` - scroll left as with |zH|.
--- - `mappings.scroll_right` - scroll right as with |zL|.
--- - `mappings.scroll_up` - when matches are shown, go up by the amount of
--- visible matches. In preview and info view - scroll up as with |CTRL-B|.
---
--- ## Stop ~
--- *MiniPick-actions-stop*
---
--- `mappings.stop` stops the picker. <C-c> also always stops the picker.
---
---
--- ## Toggle ~
--- *MiniPick-actions-toggle*
---
--- Toggle action is a way to change view: show if target is not shown, reset to
--- main view otherwise.
---
--- - `mappings.toggle_info` - toggle info view.
--- - `mappings.toggle_preview` - toggle preview.
---
--- Note:
--- - Updating query in any way resets window view to show matches.
--- - Moving current item focus keeps preview or info view with updated item.
---
--- # Custom ~
--- *MiniPick-actions-custom*
---
--- Along with built-in actions, users can define custom actions. This can be
--- done by supplying custom elements to `mappings` table. The field defines action
--- name (used to infer an action description in info view). The value is a table
--- with the following fields:
--- - <char> `(string)` - single character acting as action trigger.
--- - <func> `(function)` - callable to be executed without arguments after
--- user presses <char>. Its return value is treated as "should stop picker
--- after execution", i.e. returning nothing, `nil`, or `false` continues
--- picker while everything else (prefer `true`) stops it.
---
--- Example of `execute` custom mapping: >lua
---
--- execute = {
--- char = '<C-e>',
--- func = function() vim.cmd(vim.fn.input('Execute: ')) end,
--- }
--- <
---@tag MiniPick-actions
--- # Disable icons ~
---
--- Disable icons in |MiniPick.builtin| pickers related to paths: >lua
---
--- local pick = require('mini.pick')
--- pick.setup({ source = { show = pick.default_show } })
--- <
--- # Switch toggle and move keys ~
--- >lua
---
--- require('mini.pick').setup({
--- mappings = {
--- toggle_info = '<C-k>',
--- toggle_preview = '<C-p>',
--- move_down = '<Tab>',
--- move_up = '<S-Tab>',
--- }
--- })
--- <
--- # Different window styles: ~
--- >lua
---
--- -- Different border
--- { window = { config = { border = 'double' } } }
---
--- -- "Cursor tooltip"
--- {
--- window = {
--- config = {
--- relative = 'cursor', anchor = 'NW',
--- row = 0, col = 0, width = 40, height = 20,
--- },
--- },
--- }
---
--- -- Centered on screen
--- local win_config = function()
--- local height = math.floor(0.618 * vim.o.lines)
--- local width = math.floor(0.618 * vim.o.columns)
--- return {
--- anchor = 'NW', height = height, width = width,
--- row = math.floor(0.5 * (vim.o.lines - height)),
--- col = math.floor(0.5 * (vim.o.columns - width)),
--- }
--- end
--- { window = { config = win_config } }
--- <
---@tag MiniPick-examples
---@alias __pick_builtin_opts table|nil Options forwarded to |MiniPick.start()|.
---@alias __pick_builtin_local_opts table|nil Options defining behavior of this particular picker.
---@alias __pick_builtin_grep_globs <globs> `(table)` - array of string glob patterns to restrict search to
--- matching files. Supported only by "rg" and "git" tools, respects their
--- specific glob syntax and effects. Default: `{}` (no restriction).
--- Example: `{ '*.lua', 'lua/**' }` for Lua files and files in "lua" directory.
---@diagnostic disable:undefined-field
---@diagnostic disable:discard-returns
---@diagnostic disable:unused-local
---@diagnostic disable:cast-local-type
-- Module definition ==========================================================
local MiniPick = {}
local H = {}
--- Module setup
---
--- # :Pick ~
--- *:Pick*
---
--- Calling this function creates a `:Pick` user command. It takes picker name
--- from |MiniPick.registry| as mandatory first argument and executes it with
--- following (expanded, |expandcmd()|) |<f-args>| combined in a single table.
--- To add custom pickers, update |MiniPick.registry|.
---
--- Example: >vim
---
--- :Pick files tool='git'
--- :Pick grep pattern='<cword>'
--- <
---
--- It also sets custom |vim.ui.select()| implementation to use the module.
--- See |MiniPick.ui_select()|.
---
---@param config table|nil Module config table. See |MiniPick.config|.
---
---@usage >lua
--- require('mini.pick').setup() -- use default config
--- -- OR
--- require('mini.pick').setup({}) -- replace {} with your config table
--- <
MiniPick.setup = function(config)
-- Export module
_G.MiniPick = MiniPick
-- 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()
-- Create user commands
H.create_user_commands()
-- Disable terminal emulator's pasting with active picker
local paste_orig = vim.paste
vim.paste = function(...)
if not MiniPick.is_picker_active() then return paste_orig(...) end
H.notify('Use `mappings.paste` (`<C-r>` by default) with "*" or "+" register.', 'HINT')
end
-- Set custom implementation
vim.ui.select = MiniPick.ui_select
end
--stylua: ignore
--- Defaults ~
---@eval return MiniDoc.afterlines_to_code(MiniDoc.current.eval_section)
---@text # Delays ~
---
--- `config.delay` defines plugin delays (in ms). All should be strictly positive.
---
--- `delay.async` is a delay between forcing asynchronous behavior. This usually
--- means making screen redraws and utilizing |MiniPick.poke_is_picker_active()|
--- (for example, to stop current matching if query has updated).
--- Smaller values give smoother user experience at the cost of more computations.
---
--- `delay.busy` is a delay between when some computation starts and showing
--- visual feedback about it by making window border to have `MiniPickBorderBusy`
--- highlight group.
--- Smaller values will give feedback faster at the cost of feeling like flicker.
---
--- # Mappings ~
---
--- `config.mappings` defines keys for special actions to be triggered after certain
--- keys. See |MiniPick-actions| for more information.
---
--- # Options ~
---
--- `config.options` contains some general purpose options.
---
--- `options.content_from_bottom` is a boolean indicating whether content should be
--- shown from bottom to top. That means that best matches will be shown at
--- the bottom. Note: for better experience use Neovim>=0.10, which has floating
--- window footer capability. Default: `false`.
---
--- `options.use_cache` is a boolean indicating whether match results should be
--- cached per prompt (i.e. concatenated query). This results into faster response
--- on repeated prompts (like when deleting query entries) at the cost of using
--- more memory. Default: `false`.
---
--- # Source ~
---
--- `config.source` defines fallbacks for source specification. For example, this
--- can be used to change default `match` to use different implementation or `show`
--- to not show icons for some |MiniPick.builtin| pickers (see |MiniPick-examples|).
--- See |MiniPick-source| for more information.
---
--- # Window ~
---
--- `config.window` contains window specific configurations.
---
--- `window.config` defines a (parts of) default floating window config for the main
--- picker window. This can be either a table overriding some parts or a callable
--- returning such table. See |MiniPick-examples| for some examples.
---
--- `window.prompt_caret` defines how caret is displayed in window's prompt.
--- Default: '▏'.
---
--- `window.prompt_prefix` defines what prefix is used in window's prompt.
--- Default: '> '.
MiniPick.config = {
-- Delays (in ms; should be at least 1)
delay = {
-- Delay between forcing asynchronous behavior
async = 10,
-- Delay between computation start and visual feedback about it
busy = 50,
},
-- Keys for performing actions. See `:h MiniPick-actions`.
mappings = {
caret_left = '<Left>',
caret_right = '<Right>',
choose = '<CR>',
choose_in_split = '<C-s>',
choose_in_tabpage = '<C-t>',
choose_in_vsplit = '<C-v>',
choose_marked = '<M-CR>',
delete_char = '<BS>',
delete_char_right = '<Del>',
delete_left = '<C-u>',
delete_word = '<C-w>',
mark = '<C-x>',
mark_all = '<C-a>',
move_down = '<C-n>',
move_start = '<C-g>',
move_up = '<C-p>',
paste = '<C-r>',
refine = '<C-Space>',
refine_marked = '<M-Space>',
scroll_down = '<C-f>',
scroll_left = '<C-h>',
scroll_right = '<C-l>',
scroll_up = '<C-b>',
stop = '<Esc>',
toggle_info = '<S-Tab>',
toggle_preview = '<Tab>',
},
-- General options
options = {
-- Whether to show content from bottom to top
content_from_bottom = false,
-- Whether to cache matches (more speed and memory on repeated prompts)
use_cache = false,
},
-- Source definition. See `:h MiniPick-source`.
source = {
items = nil,
name = nil,
cwd = nil,
match = nil,
show = nil,
preview = nil,
choose = nil,
choose_marked = nil,
},
-- Window related options
window = {
-- Float window config (table or callable returning it)
config = nil,
-- String to use as caret in prompt
prompt_caret = '▏',
-- String to use as prefix in prompt
prompt_prefix = '> ',
},
}
--minidoc_afterlines_end
--- Start picker
---
--- Notes:
--- - If there is currently an active picker, it is properly stopped and new one
--- is started "soon" in the main event-loop (see |vim.schedule()|).
--- - Current window at the moment of this function call is treated as "target".
--- It will be set back as current after |MiniPick.stop()|.
--- See |MiniPick.get_picker_state()| and |MiniPick.set_picker_target_window()|.
---
---@param opts table|nil Options. Should have same structure as |MiniPick.config|.
--- Default values are inferred from there.
--- Usually should have proper |MiniPick-source.items| defined.
---
---@return any Item which was current when picker is stopped; `nil` if aborted.
MiniPick.start = function(opts)
if MiniPick.is_picker_active() then
-- Try proper 'key query process' stop
MiniPick.stop()
-- NOTE: Needs `defer_fn()` for `stop()` to properly finish code flow and
-- not be executed before it
return vim.defer_fn(function()
-- NOTE: if `MiniPick.stop()` still didn't stop, force abort
if MiniPick.is_picker_active() then H.picker_stop(H.pickers.active, true) end
MiniPick.start(opts)
end, 0.5)
end
H.cache = {}
opts = H.validate_picker_opts(opts)
local picker = H.picker_new(opts)
H.pickers.active = picker
H.picker_set_busy(picker, true)
local items = H.expand_callable(opts.source.items)
-- - Set items on next event loop to not block when computing stritems
if H.islist(items) then vim.schedule(function() MiniPick.set_picker_items(items) end) end
H.picker_track_lost_focus(picker)
return H.picker_advance(picker)
end
--- Stop active picker
MiniPick.stop = function()
if not MiniPick.is_picker_active() then return end
H.cache.is_force_stop_advance = true
if H.cache.is_in_getcharstr then vim.api.nvim_feedkeys('\3', 't', true) end
end
--- Refresh active picker
MiniPick.refresh = function()
if not MiniPick.is_picker_active() then return end
H.picker_update(H.pickers.active, false, true)
end
--- Default match
---
--- Filter target stritems to contain query and sort from best to worst matches.
---
--- Implements default value for |MiniPick-source.match|.
---
--- By default (if no special modes apply) it does the following fuzzy matching:
---
--- - Stritem contains query if it contains all its elements verbatim in the same
--- order (possibly with gaps, i.e. not strictly one after another).
--- Note: empty query and empty string element is contained in any string.
---
--- - Sorting is done with the following ordering (same as in |mini.fuzzy|):
--- - The smaller the match width (end column minus start column) the better.
--- - Among same match width, the smaller start column the better.
--- - Among same match width and start column, preserve original order.
---
--- Notes:
--- - Most common interactive usage results into `query` containing one typed
--- character per element.
---
--- # Special modes ~
---
--- - Forced modes:
--- - Query starts with "*": match the rest fuzzy (without other modes).
--- - Query starts with "'": match the rest exactly (without gaps).
---
--- - Place modes:
--- - Query starts with '^': match the rest exactly at start.
--- - Query ends with '$': match the rest exactly at end.
--- - Both modes can be used simultaneously.
---
--- - Grouped: query contains at least one whitespace element. Output is computed
--- as if query is split at whitespace indexes with concatenation between them.
---
--- Precedence of modes:
--- "forced exact" = "forced fuzzy" > "place start/end" > "grouped" > "default"
---
--- # Examples ~
---
--- Assuming `stritems` are `{ '_abc', 'a_bc', 'ab_c', 'abc_' }`, here are some
--- example matches based on prompt (concatenated query): >
---
--- | Prompt | Matches |
--- |--------|------------------------|
--- | abc | All |
--- | *abc | All |
--- | | |
--- | 'abc | abc_, _abc |
--- | *'abc | None (no "'" in items) |
--- | | |
--- | ^abc | abc_ |
--- | *^abc | None (no "^" in items) |
--- | | |
--- | abc$ | _abc |
--- | *abc$ | None (no "$" in items) |
--- | | |
--- | ab c | abc_, _abc, ab_c |
--- | *ab c | None (no " " in items) |
--- <
--- Having query `{ 'ab', 'c' }` is the same as "ab c" prompt.
---
--- You can have a feel of how this works with this command: >lua
---
--- MiniPick.start({ source = { items = { '_abc', 'a_bc', 'ab_c', 'abc_' } } })
--- <
---@param stritems table Array of all stritems.
---@param inds table Array of `stritems` indexes to match. All of them should point
0