FFFF mini.nvim/lua/mini/surround.lua at main · tristan957/mini.nvim · GitHub
[go: up one dir, main page]

Skip to content

Latest commit

 

History

History
2355 lines (2070 loc) · 98.7 KB

surround.lua

File metadata and controls

2355 lines (2070 loc) · 98.7 KB
--- function()
--- local n_lines = vim.fn.line('$')
--- return {
--- left = {
--- from = { line = 1, col = 1 },
--- to = { line = 1, col = vim.fn.getline(1):len() }
--- },
--- right = {
--- from = { line = n_lines, col = 1 },
--- to = { line = n_lines, col = vim.fn.getline(n_lines):len() }
--- },
--- }
--- end
--- <
--- - Array of region pairs. Useful for incorporating other instruments,
--- like treesitter (see |MiniSurround.gen_spec.input.treesitter()|). The
--- best region pair will be picked in the same manner as with composed
--- pattern (respecting options `n_lines`, `search_method`, etc.) using
--- output region (from start of left region to end of right region).
--- Example using edges of "best" line with display width more than 80: >lua
---
--- function()
--- local make_line_region_pair = function(n)
--- local left = { line = n, col = 1 }
--- local right = { line = n, col = vim.fn.getline(n):len() }
--- return {
--- left = { from = left, to = left },
--- right = { from = right, to = right },
--- }
--- end
---
--- local res = {}
--- for i = 1, vim.fn.line('$') do
--- if vim.fn.getline(i):len() > 80 then
--- table.insert(res, make_line_region_pair(i))
--- end
--- end
--- return res
--- end
--- <
--- - If there is a callable instead of assumed string pattern, it is expected
--- to have signature `(line, init)` and behave like `pattern:find()`.
--- It should return two numbers representing span in `line` next after
--- or at `init` (`nil` if there is no such span).
--- !IMPORTANT NOTE!: it means that output's `from` shouldn't be strictly
--- to the left of `init` (it will lead to infinite loop). Not allowed as
--- last item (as it should be pattern with captures).
--- Example of matching only balanced parenthesis with big enough width: >lua
---
--- {
--- '%b()',
--- function(s, init)
--- if init > 1 or s:len() < 5 then return end
--- return 1, s:len()
--- end,
--- '^.().*().$'
--- }
--- <
--- More examples: >lua
---
--- -- Pair of balanced brackets from set (used for builtin `b` identifier)
--- { { '%b()', '%b[]', '%b{}' }, '^.().*().$' }
---
--- -- Lua block string
--- { '%[%[().-()%]%]' }
--- <
--- See |MiniSurround.gen_spec| for function wrappers to create commonly used
--- surrounding specifications.
---
--- # Output surrounding ~
---
--- Specification for output can be either a table with <left> and <right> fields,
--- or a callable returning such table (will be called with no arguments).
--- Strings can contain new lines character "\n" to add multiline parts.
---
--- Examples: >lua
---
--- -- Lua block string
--- { left = '[[', right = ']]' }
---
--- -- Brackets on separate lines (indentation is not preserved)
--- { left = '(\n', right = '\n)' }
---
--- -- Function call
--- function()
--- local function_name = MiniSurround.user_input('Function name')
--- return { left = function_name .. '(', right = ')' }
--- end
--- <
---@tag MiniSurround-surrounding-specification
--- |[count]| is supported by all actions in the following ways:
---
--- - In add, two types of `[count]` is supported in Normal mode:
--- `[count1]sa[count2][textobject]`. The `[count1]` defines how many times
--- left and right parts of output surrounding will be repeated and `[count2]` is
--- used for textobject.
--- In Visual mode `[count]` is treated as `[count1]`.
--- Example: `2sa3aw)` and `v3aw2sa)` will result into textobject `3aw` being
--- surrounded by `((` and `))`.
---
--- - In delete/replace/find/highlight `[count]` means "find n-th surrounding
--- and execute operator on it".
--- Example: `2sd)` on line `(a(b(c)b)a)` with cursor on `c` will result into
--- `(ab(c)ba)` (and not in `(abcba)` if it would have meant "delete n times").
---@tag MiniSurround-count-with-actions
--- Search for the input surrounding relies on these principles:
--- - Input surrounding specification is constructed based on surrounding
--- identifier (see |MiniSurround-surrounding-specification|).
--- - General search is done by converting some 2d buffer region (neighborhood
--- of reference region) into 1d string (each line is appended with `\n`).
--- Then search for a best span matching specification is done inside string
--- (see |MiniSurround-glossary|). After that, span is converted back into 2d
--- region. Note: first search is done inside reference region lines, and
--- only after that - inside its neighborhood within `config.n_lines` (see
--- |MiniSurround.config|).
--- - The best matching span is chosen by iterating over all spans matching
--- surrounding specification and comparing them with "current best".
--- Comparison also depends on reference region (tighter covering is better,
--- otherwise closer is better) and search method (if span is even considered).
--- - Extract pair of spans (for left and right regions in region pair) based
--- on extraction pattern (last item in nested pattern).
--- - For |[count]| greater than 1, steps are repeated with current best match
--- becoming reference region. One such additional step is also done if final
--- region is equal to reference region.
---
--- Notes:
--- - Iteration over all matched spans is done in depth-first fashion with
--- respect to nested pattern.
--- - It is guaranteed that span is compared only once.
--- - For the sake of increasing functionality, during iteration over all
--- matching spans, some Lua patterns in composed pattern are handled
--- specially.
--- - `%bxx` (`xx` is two identical characters). It denotes balanced pair
--- of identical characters and results into "paired" matches. For
--- example, `%b""` for `"aa" "bb"` would match `"aa"` and `"bb"`, but
--- not middle `" "`.
--- - `x.-y` (`x` and `y` are different strings). It results only in matches with
--- smallest width. For example, `e.-o` for `e e o o` will result only in
--- middle `e o`. Note: it has some implications for when parts have
--- quantifiers (like `+`, etc.), which usually can be resolved with
--- frontier pattern `%f[]`.
---@tag MiniSurround-search-algorithm
-- Module definition ==========================================================
local MiniSurround = {}
local H = {}
--- Module setup
---
---@param config table|nil Module config table. See |MiniSurround.config|.
---
---@usage >lua
--- require('mini.surround').setup() -- use default config
--- -- OR
--- require('mini.surround').setup({}) -- replace {} with your config table
--- <
MiniSurround.setup = function(config)
-- Export module
_G.MiniSurround = MiniSurround
-- Setup config
config = H.setup_config(config)
-- Apply config
H.apply_config(config)
-- Define behavior
H.create_autocommands()
-- Create default highlighting
H.create_default_hl()
end
--- Defaults ~
---@eval return MiniDoc.afterlines_to_code(MiniDoc.current.eval_section)
---@text # Mappings ~
---
--- `config.mappings` defines what mappings are set up for particular actions.
--- By default it uses "prefix style" left hand side starting with "s" (for
--- "surround"): `sa` - "surround add", `sd` - "surround delete", etc.
---
--- Note: if any of the mappings start with "s" (as is by default), it is mapped
--- to |<Nop>| to prevent accidental trigger of built-in |s| (can happen if there
--- is a long enough delay between pressing "s" and the next key). Use `cl` instead.
---
--- # Custom surroundings ~
---
--- User can define own surroundings by supplying `config.custom_surroundings`.
--- It should be a **table** with keys being single character surrounding
--- identifier (supported by |getcharstr()|) and values - surround specification
--- (see |MiniSurround-surrounding-specification|).
---
--- General recommendations:
--- - In `config.custom_surroundings` only some data can be defined (like only
--- `output`). Other fields will be taken from builtin surroundings.
--- - Function returning surround info at <input> or <output> fields of
--- specification is helpful when user input is needed (like asking for
--- function name). Use |input()| or |MiniSurround.user_input()|. Return
--- `nil` to stop any current surround operation.
--- - Keys should use character representation which can be |getcharstr()| output.
--- For example, `'\r'` and not `'<CR>'`.
---
--- Examples of using `config.custom_surroundings` (see more examples at
--- |MiniSurround.gen_spec|): >lua
---
--- local surround = require('mini.surround')
--- surround.setup({
--- custom_surroundings = {
--- -- Make `)` insert parts with spaces. `input` pattern stays the same.
--- [')'] = { output = { left = '( ', right = ' )' } },
---
--- -- Use function to compute surrounding info
--- ['*'] = {
--- input = function()
--- local n_star = MiniSurround.user_input('Number of * to find')
--- local many_star = string.rep('%*', tonumber(n_star) or 1)
--- return { many_star .. '().-()' .. many_star }
--- end,
--- output = function()
--- local n_star = MiniSurround.user_input('Number of * to output')
--- local many_star = string.rep('*', tonumber(n_star) or 1)
--- return { left = many_star, right = many_star }
--- end,
--- },
--- },
--- })
---
--- -- Create custom surrounding for Lua's block string `[[...]]`
--- -- Use this inside autocommand or 'after/ftplugin/lua.lua' file
--- vim.b.minisurround_config = {
--- custom_surroundings = {
--- s = {
--- input = { '%[%[().-()%]%]' },
--- output = { left = '[[', right = ']]' },
--- },
--- },
--- }
--- <
--- # Respect selection type ~
---
--- Boolean option `config.respect_selection_type` controls whether to respect
--- selection type when adding and deleting surrounding. When enabled:
--- - Linewise adding places surroundings on separate lines while indenting
--- surrounded lines ones.
--- - Deleting surroundings which look like they were the result of linewise
--- adding will act to revert it: delete lines with surroundings and dedent
--- surrounded lines ones.
--- - Blockwise adding places surroundings on whole edges, not only start and
--- end of selection. Note: it doesn't really work outside of text and in
--- presence of multibyte characters; and probably won't due to
--- implementation difficulties.
---
--- # Search method ~
---
--- Value of `config.search_method` defines how best match search is done.
--- Based on its value, one of the following matches will be selected:
--- - Covering match. Left/right edge is before/after left/right edge of
--- reference region.
--- - Previous match. Left/right edge is before left/right edge of reference
--- region.
--- - Next match. Left/right edge is after left/right edge of reference region.
--- - Nearest match. Whichever is closest among previous and next matches.
---
--- Possible values are:
--- - `'cover'` (default) - use only covering match. Don't use either previous or
--- next; report that there is no surrounding found.
--- - `'cover_or_next'` - use covering match. If not found, use next.
--- - `'cover_or_prev'` - use covering match. If not found, use previous.
--- - `'cover_or_nearest'` - use covering match. If not found, use nearest.
--- - `'next'` - use next match.
--- - `'previous'` - use previous match.
--- - `'nearest'` - use nearest match.
---
--- Note: search is first performed on the reference region lines and only
--- after failure - on the whole neighborhood defined by `config.n_lines`. This
--- means that with `config.search_method` not equal to `'cover'`, "previous"
--- or "next" surrounding will end up as search result if they are found on
--- first stage although covering match might be found in bigger, whole
--- neighborhood. This design is based on observation that most of the time
--- operation is done within reference region lines (usually cursor line).
---
--- Here is an example of how replacing `)` with `]` surrounding is done based
--- on a value of `'config.search_method'` when cursor is inside `bbb` word:
--- - `'cover'`: `(a) bbb (c)` -> `(a) bbb (c)` (with message)
--- - `'cover_or_next'`: `(a) bbb (c)` -> `(a) bbb [c]`
--- - `'cover_or_prev'`: `(a) bbb (c)` -> `[a] bbb (c)`
--- - `'cover_or_nearest'`: depends on cursor position.
--- For first and second `b` - as in `cover_or_prev` (as previous match is
--- nearer), for third - as in `cover_or_next` (as next match is nearer).
--- - `'next'`: `(a) bbb (c)` -> `(a) bbb [c]`. Same outcome for `(bbb)`.
--- - `'prev'`: `(a) bbb (c)` -> `[a] bbb (c)`. Same outcome for `(bbb)`.
--- - `'nearest'`: depends on cursor position (same as in `'cover_or_nearest'`).
---
--- # Search suffixes ~
---
--- To provide more searching possibilities, 'mini.surround' creates extended
--- mappings force "prev" and "next" methods for particular search. It does so
--- by appending mapping with certain suffix: `config.mappings.suffix_last` for
--- mappings which will use "prev" search method, `config.mappings.suffix_next`
--- - "next" search method.
---
--- Notes:
--- - It creates new mappings only for actions involving surrounding search:
--- delete, replace, find (right and left), highlight.
--- - All new mappings behave the same way as if `config.search_method` is set
--- to certain search method. They preserve dot-repeat support, respect |[count]|.
--- - Supply empty string to disable creation of corresponding set of mappings.
---
--- Example with default values (`n` for `suffix_next`, `l` for `suffix_last`)
--- and initial line `(aa) (bb) (cc)`.
--- - Typing `sdn)` with cursor inside `(aa)` results into `(aa) bb (cc)`.
--- - Typing `sdl)` with cursor inside `(cc)` results into `(aa) bb (cc)`.
--- - Typing `2srn)]` with cursor inside `(aa)` results into `(aa) (bb) [cc]`.
---
--- # Setup similar to 'tpope/vim-surround' ~
--- *MiniSurround-vim-surround-config*
---
--- This module is primarily designed after 'machakann/vim-sandwich'. To get
--- behavior closest to 'tpope/vim-surround' (but not identical), use this setup: >lua
---
--- require('mini.surround').setup({
--- mappings = {
--- add = 'ys',
--- delete = 'ds',
--- find = '',
--- find_left = '',
--- highlight = '',
--- replace = 'cs',
---
--- -- Add this only if you don't want to use extended mappings
--- suffix_last = '',
--- suffix_next = '',
--- },
--- search_method = 'cover_or_next',
--- })
---
--- -- Remap adding surrounding to Visual mode selection
--- vim.keymap.del('x', 'ys')
--- vim.keymap.set('x', 'S', [[:<C-u>lua MiniSurround.add('visual')<CR>]], { silent = true })
---
--- -- Make special mapping for "add surrounding for line"
--- vim.keymap.set('n', 'yss', 'ys_', { remap = true })
--- <
MiniSurround.config = {
-- Add custom surroundings to be used on top of builtin ones. For more
-- information with examples, see `:h MiniSurround.config`.
custom_surroundings = nil,
-- Duration (in ms) of highlight when calling `MiniSurround.highlight()`
highlight_duration = 500,
-- Module mappings. Use `''` (empty string) to disable one.
mappings = {
add = 'sa', -- Add surrounding in Normal and Visual modes
delete = 'sd', -- Delete surrounding
find = 'sf', -- Find surrounding (to the right)
find_left = 'sF', -- Find surrounding (to the left)
highlight = 'sh', -- Highlight surrounding
replace = 'sr', -- Replace surrounding
suffix_last = 'l', -- Suffix to search with "prev" method
suffix_next = 'n', -- Suffix to search with "next" method
},
-- Number of lines within which surrounding is searched
n_lines = 20,
-- Whether to respect selection type:
-- - Place surroundings on separate lines in linewise mode.
-- - Place surroundings on each line in blockwise mode.
respect_selection_type = false,
-- How to search for surrounding (first inside current line, then inside
-- neighborhood). One of 'cover', 'cover_or_next', 'cover_or_prev',
-- 'cover_or_nearest', 'next', 'prev', 'nearest'. For more details,
-- see `:h MiniSurround.config`.
search_method = 'cover',
-- Whether to disable showing non-error feedback
-- This also affects (purely informational) helper messages shown after
-- idle time if user input is required.
silent = false,
}
--minidoc_afterlines_end
-- Module functionality =======================================================
--- Add surrounding
---
--- No need to use it directly, everything is setup in |MiniSurround.setup()|.
---
---@param mode string Mapping mode (normal by default).
MiniSurround.add = function(mode)
-- Needed to disable in visual mode
if H.is_disabled() then return '<Esc>' end
-- Get marks' positions based on current mode
local marks = H.get_marks_pos(mode)
-- Get surround info. Try take from cache only in not visual mode (as there
-- is no intended dot-repeatability).
local surr_info
if mode == 'visual' then
surr_info = H.get_surround_spec('output', false)
else
surr_info = H.get_surround_spec('output', true)
end
if surr_info == nil then return '<Esc>' end
-- Extend parts based on provided `[count]` before operator (if this is not
-- from dot-repeat and was done already)
if not surr_info.did_count then
local count = H.cache.count or vim.v.count1
surr_info.left, surr_info.right = surr_info.left:rep(count), surr_info.right:rep(count)
surr_info.did_count = true
end
-- Add surrounding.
-- Possibly deal with linewise and blockwise addition separately
local respect_selection_type = H.get_config().respect_selection_type
if not respect_selection_type or marks.selection_type == 'charwise' then
-- Begin insert from right to not break column numbers
-- Insert after the right mark (`+ 1` is for that)
H.region_replace({ from = { line = marks.second.line, col = marks.second.col + 1 } }, surr_info.right)
H.region_replace({ from = marks.first }, surr_info.left)
-- Set cursor to be on the right of left surrounding
H.set_cursor(marks.first.line, marks.first.col + surr_info.left:len())
return
end
if marks.selection_type == 'linewise' then
local from_line, to_line = marks.first.line, marks.second.line
-- Save current range indent and indent surrounded lines
local init_indent = H.get_range_indent(from_line, to_line)
H.shift_indent('>', from_line, to_line)
-- Put cursor on the start of first surrounded line
H.set_cursor_nonblank(from_line)
-- Put surroundings on separate lines
vim.fn.append(to_line, init_indent .. surr_info.right)
vim.fn.append(from_line - 1, init_indent .. surr_info.left)
return
end
if marks.selection_type == 'blockwise' then
-- NOTE: this doesn't work with mix of multibyte and normal characters, as
-- well as outside of text lines.
local from_col, to_col = marks.first.col, marks.second.col
-- - Ensure that `to_col` is to the right of `from_col`. Can be not the
-- case if visual block was selected from "south-west" to "north-east".
from_col, to_col = math.min(from_col, to_col), math.max(from_col, to_col)
for i = marks.first.line, marks.second.line do
H.region_replace({ from = { line = i, col = to_col + 1 } }, surr_info.right)
H.region_replace({ from = { line = i, col = from_col } }, surr_info.left)
end
H.set_cursor(marks.first.line, from_col + surr_info.left:len())
return
end
end
--- Delete surrounding
---
--- No need to use it directly, everything is setup in |MiniSurround.setup()|.
MiniSurround.delete = function()
-- Find input surrounding region
local surr = H.find_surrounding(H.get_surround_spec('input', true))
if surr == nil then return '<Esc>' end
-- Delete surrounding region. Begin with right to not break column numbers.
H.region_replace(surr.right, {})
H.region_replace(surr.left, {})
-- Set cursor to be on the right of deleted left surrounding
local from = surr.left.from
H.set_cursor(from.line, from.col)
-- Possibly tweak deletion of linewise surrounding. Should act as reverse to
-- linewise addition.
if not H.get_config().respect_selection_type then return end
local from_line, to_line = surr.left.from.line, surr.right.from.line
local is_linewise_delete = from_line < to_line and H.is_line_blank(from_line) and H.is_line_blank(to_line)
if is_linewise_delete then
-- Dedent surrounded lines
H.shift_indent('<', from_line, to_line)
-- Place cursor on first surrounded line
H.set_cursor_nonblank(from_line + 1)
-- Delete blank lines left after deleting surroundings
local buf_id = vim.api.nvim_get_current_buf()
vim.fn.deletebufline(buf_id, to_line)
vim.fn.deletebufline(buf_id, from_line)
end
end
--- Replace surrounding
---
--- No need to use it directly, everything is setup in |MiniSurround.setup()|.
MiniSurround.replace = function()
-- Find input surrounding region
local surr = H.find_surrounding(H.get_surround_spec('input', true))
if surr == nil then return '<Esc>' end
-- Get output surround info
local new_surr_info = H.get_surround_spec('output', true)
if new_surr_info == nil then return '<Esc>' end
-- Replace by parts starting from right to not break column numbers
H.region_replace(surr.right, new_surr_info.right)
H.region_replace(surr.left, new_surr_info.left)
-- Set cursor to be on the right of left surrounding
local from = surr.left.from
H.set_cursor(from.line, from.col + new_surr_info.left:len())
end
--- Find surrounding
---
--- No need to use it directly, everything is setup in |MiniSurround.setup()|.
MiniSurround.find = function()
-- Find surrounding region
local surr = H.find_surrounding(H.get_surround_spec('input', true))
if surr == nil then return end
-- Make array of unique positions to cycle through
local pos_array = H.surr_to_pos_array(surr)
-- Cycle cursor through positions
local dir = H.cache.direction or 'right'
H.cursor_cycle(pos_array, dir)
-- Open 'enough folds' to show cursor
vim.cmd('normal! zv')
end
--- Highlight surrounding
---
--- No need to use it directly, everything is setup in |MiniSurround.setup()|.
MiniSurround.highlight = function()
-- Find surrounding region
local surr = H.find_surrounding(H.get_surround_spec('input', true))
if surr == nil then return end
-- Highlight surrounding region
local config = H.get_config()
local buf_id = vim.api.nvim_get_current_buf()
H.region_highlight(buf_id, surr.left)
H.region_highlight(buf_id, surr.right)
vim.defer_fn(function()
H.region_unhighlight(buf_id, surr.left)
H.region_unhighlight(buf_id, surr.right)
end, config.highlight_duration)
end
--- Update `MiniSurround.config.n_lines` from user input
---
--- Mapping example: >lua
---
--- vim.keymap.set('n', 'sn', '<Cmd>lua MiniSurround.update_n_lines()<CR>')
--- <
MiniSurround.update_n_lines = function()
local n_lines = MiniSurround.user_input('New number of neighbor lines', MiniSurround.config.n_lines)
MiniSurround.config.n_lines = math.floor(tonumber(n_lines) or MiniSurround.config.n_lines)
end
--- Ask user for input
---
--- This is mainly a wrapper for |input()| which allows empty string as input,
--- cancelling with `<Esc>` and `<C-c>`, and slightly modifies prompt. Use it
--- to ask for input inside function custom surrounding (see |MiniSurround.config|).
MiniSurround.user_input = function(prompt, text)
-- Major issue with both `vim.fn.input()` is that the only way to distinguish
-- cancelling with `<Esc>` and entering empty string with immediate `<CR>` is
-- through `cancelreturn` option (see `:h input()`). In that case the return
-- of `cancelreturn` will mean actual cancel, which removes possibility of
-- using that string. Although doable with very obscure string, this is not
-- very clean.
-- Overcome this by adding temporary keystroke listener.
local on_key = vim.on_key or vim.register_keystroke_callback
local was_cancelled = false
on_key(function(key)
if key == vim.api.nvim_replace_termcodes('<Esc>', true, true, true) then was_cancelled = true end
end, H.ns_id.input)
-- Ask for input
-- NOTE: it would be GREAT to make this work with `vim.ui.input()` but I
-- didn't find a way to make it work without major refactor of whole module.
-- The main issue is that `vim.ui.input()` is designed to perform action in
-- callback and current module design is to get output immediately. Although
-- naive approach of
-- `local res; vim.ui.input({...}, function(input) res = input end)`
-- works in default `vim.ui.input`, its reimplementations can return from it
-- immediately and proceed in main event loop. Couldn't find a relatively
-- simple way to stop execution of this current function until `ui.input()`'s
-- callback finished execution.
local opts = { prompt = '(mini.surround) ' .. prompt .. ': ', default = text or '' }
vim.cmd('echohl Question')
-- Use `pcall` to allow `<C-c>` to cancel user input
local ok, res = pcall(vim.fn.input, opts)
vim.cmd([[echohl None | echo '' | redraw]])
-- Stop key listening
on_key(nil, H.ns_id.input)
if not ok or was_cancelled then return end
return res
end
--- Generate common surrounding specifications
---
--- This is a table with two sets of generator functions: <input> and <output>
--- (currently empty). Each is a table with function values generating
--- corresponding surrounding specification.
---
---@seealso |MiniAi.gen_spec|
MiniSurround.gen_spec = { input = {}, output = {} }
--- Treesitter specification for input surrounding
---
--- This is a specification in function form. When called with a pair of
--- treesitter captures, it returns a specification function outputting an
--- array of region pairs derived from <outer> and <inner> captures. It first
--- searches for all matched nodes of outer capture and then completes each one
--- with the biggest match of inner capture inside that node (if any). The result
--- region pair is a difference between regions of outer and inner captures.
---
--- In order for this to work, apart from working treesitter parser for desired
--- language, user should have a reachable language-specific 'textobjects'
--- query (see |vim.treesitter.query.get()|).
--- The most straightforward way for this is to have 'textobjects.scm' query
--- file with treesitter captures stored in some recognized path. This is
--- primarily designed to be compatible with plugin
--- 'nvim-treesitter/nvim-treesitter-textobjects', but can be used without it.
---
--- Two most common approaches for having a query file:
--- - Install 'nvim-treesitter/nvim-treesitter-textobjects'. It has curated and
--- well maintained builtin query files for many languages with a standardized
--- capture names, like `call.outer`, `call.inner`, etc.
--- - Manually create file 'after/queries/<language name>/textobjects.scm' in
--- your |$XDG_CONFIG_HOME| directory. It should contain queries with
--- captures (later used to define surrounding parts). See |lua-treesitter-query|.
--- To verify that query file is reachable, run (example for "lua" language,
--- output should have at least an intended file): >vim
---
--- :lua print(vim.inspect(vim.treesitter.query.get_files('lua','textobjects')))
--- <
--- Example configuration for function definition textobject with
--- 'nvim-treesitter/nvim-treesitter-textobjects' captures: >lua
---
--- local ts_input = require('mini.surround').gen_spec.input.treesitter
--- require('mini.surround').setup({
--- custom_surroundings = {
--- -- Use tree-sitter to search for function call
--- f = {
--- input = ts_input({ outer = '@call.outer', inner = '@call.inner' })
--- },
--- }
--- })
--- <
--- Notes:
0