*mini.surround* Surround actions
MIT License Copyright (c) 2021 Evgeni Chasnovski
------------------------------------------------------------------------------
*MiniSurround*
Fast and feature-rich surrounding. Can be configured to have experience
similar to [tpope/vim-surround](https://github.com/tpope/vim-surround)
(see |MiniSurround-vim-surround-config|).
Features:
- Actions (text editing actions are dot-repeatable out of the box and
respect |[count]|) with configurable mappings:
- Add surrounding with `sa` (in visual mode or on motion).
- Delete surrounding with `sd`.
- Replace surrounding with `sr`.
- Find surrounding with `sf` or `sF` (move cursor right or left).
- Highlight surrounding with `sh`.
- Surrounding is identified by a single character as both "input" (in
`delete` and `replace` start, `find`, and `highlight`) and "output" (in
`add` and `replace` end):
- `f` - function call (string of alphanumeric symbols or `_` or `.`
followed by balanced `()`). In "input" finds function call, in
"output" prompts user to enter function name.
- `t` - tag. In "input" finds tag with same identifier, in "output"
prompts user to enter tag name with possible attributes.
- All symbols in brackets `()`, `[]`, `{}`, `<>`. In "input" represents
balanced brackets (open - with whitespace pad, close - without), in
"output" - left and right parts of brackets.
- `?` - interactive. Prompts user to enter left and right parts.
- All other single character identifiers (supported by |getcharstr()|)
represent surrounding with identical left and right parts.
- Configurable search methods to find not only covering but possibly next,
previous, or nearest surrounding. See more in |MiniSurround.config|.
- All actions involving finding surrounding (delete, replace, find,
highlight) can be used with suffix that changes search method to find
previous/last. See more in |MiniSurround.config|.
Known issues which won't be resolved:
- Search for surrounding is done using Lua patterns (regex-like approach).
So certain amount of false positives should be expected.
- When searching for "input" surrounding, there is no distinction if it is
inside string or comment. So in this case there will be not proper match
for a function call: `f(a = ")", b = 1)`.
- Tags are searched using regex-like methods, so issues are inevitable.
Overall it is pretty good, but certain cases won't work. Like self-nested
tags won't match correctly on both ends: ``.
# Setup ~
This module needs a setup with `require('mini.surround').setup({})`
(replace `{}` with your `config` table). It will create global Lua table
`MiniSurround` which you can use for scripting or manually (with
`:lua MiniSurround.*`).
See |MiniSurround.config| for `config` structure and default values.
You can override runtime config settings locally to buffer inside
`vim.b.minisurround_config` which should have same structure as
`MiniSurround.config`. See |mini.nvim-buffer-local-config| for more details.
To stop module from showing non-error feedback, set `config.silent = true`.
# Example usage ~
Regular mappings:
- `saiw)` - add (`sa`) for inner word (`iw`) parenthesis (`)`).
- `saiw?[[]]` - add (`sa`) for inner word (`iw`) interactive
surrounding (`?`): `[[` for left and `]]` for right.
- `2sdf` - delete (`sd`) second (`2`) surrounding function call (`f`).
- `sr)tdiv` - replace (`sr`) surrounding parenthesis (`)`) with tag
(`t`) with identifier `div` (`div` in command line prompt).
- `sff` - find right (`sf`) part of surrounding function call (`f`).
- `sh}` - highlight (`sh`) for a brief period of time surrounding curly
brackets (`}`).
Extended mappings (temporary force "prev"/"next" search methods):
- `sdnf` - delete (`sd`) next (`n`) function call (`f`).
- `srlf(` - replace (`sr`) last (`l`) function call (`f`) with padded
bracket (`(`).
- `2sfnt` - find (`sf`) second (`2`) next (`n`) tag (`t`).
- `2shl}` - highlight (`sh`) last (`l`) second (`2`) curly bracket (`}`).
# Comparisons ~
- [tpope/vim-surround](https://github.com/tpope/vim-surround):
- `vim-surround` has completely different, with other focus set of
default mappings, while |mini.surround| has a more coherent set.
- |mini.surround| supports dot-repeat, customized search path (see
|MiniSurround.config|), customized specifications (see
|MiniSurround-surrounding-specification|) allowing usage of tree-sitter
queries (see |MiniSurround.gen_spec.input.treesitter()|),
highlighting and finding surrounding, "last"/"next" extended
mappings. While `vim-surround` does not.
- [machakann/vim-sandwich](https://github.com/machakann/vim-sandwich):
- Both have same keybindings for common actions (add, delete, replace).
- Otherwise same differences as with `tpope/vim-surround` (except
dot-repeat because `vim-sandwich` supports it).
- [kylechui/nvim-surround](https://github.com/kylechui/nvim-surround):
- `nvim-surround` is designed after `tpope/vim-surround` with same
default mappings and logic, while |mini.surround| has mappings
similar to `machakann/vim-sandwich`.
- |mini.surround| has more flexible customization of input surrounding
(with composed patterns, region pair(s), search methods).
- |mini.surround| supports |[count]| in both input and output
surrounding (see |MiniSurround-count-with-actions|) while
`nvim-surround` doesn't.
- |mini.surround| supports "last"/"next" extended mappings.
- |mini.ai|:
- Both use similar logic for finding target: textobject in |mini.ai|
and surrounding pair in |mini.surround|. While |mini.ai| uses
extraction pattern for separate `a` and `i` textobjects,
|mini.surround| uses it to select left and right surroundings
(basically a difference between `a` and `i` textobjects).
- Some builtin specifications are slightly different:
- Quotes in |mini.ai| are balanced, in |mini.surround| they are not.
- The |mini.surround| doesn't have argument surrounding.
- Default behavior in |mini.ai| selects one of the edges into `a`
textobject, while |mini.surround| - both.
# Highlight groups ~
*MiniSurround-hl-groups*
- `MiniSurround` - highlighting of requested surrounding.
To change any highlight group, set it directly with |nvim_set_hl()|.
# Disabling ~
To disable, set `vim.g.minisurround_disable` (globally) or
`vim.b.minisurround_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.
------------------------------------------------------------------------------
*MiniSurround-builtin-surroundings*
This table describes all builtin surroundings along with what they
represent. Explanation:
- `Key` represents the surrounding identifier: single character which should
be typed after action mappings (see "Mappings" in |MiniSurround.config|).
- `Name` is a description of surrounding.
- `Example line` contains a string for which examples are constructed. The
`*` denotes the cursor position over `a` character.
- `Delete` shows the result of typing `sd` followed by surrounding identifier.
It aims to demonstrate "input" surrounding which is also used in replace
with `sr` (surrounding id is typed first), highlight with `sh`, find with
`sf` and `sF`.
- `Replace` shows the result of typing `sr!` followed by surrounding
identifier (with possible follow up from user). It aims to demonstrate
"output" surrounding which is also used in adding with `sa` (followed by
textobject/motion or in Visual mode).
Example: typing `sd)` with cursor on `*` (covers `a` character) changes line
`!( *a (bb) )!` into `! aa (bb) !`. Typing `sr!)` changes same initial line
into `(( aa (bb) ))`.
>
┌───┬───────────────┬───────────────┬─────────────┬─────────────────┐
│Key│ Name │ Example line │ Delete │ Replace │
├───┴───────────────┴───────────────┴─────────────┴─────────────────┤
├┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┤
│ ( │ Balanced () │ !( *a (bb) )! │ !aa (bb)! │ ( ( aa (bb) ) ) │
│ [ │ Balanced [] │ ![ *a [bb] ]! │ !aa [bb]! │ [ [ aa [bb] ] ] │
│ { │ Balanced {} │ !{ *a {bb} }! │ !aa {bb}! │ { { aa {bb} } } │
│ < │ Balanced <> │ !< *a >! │ !aa ! │ < < aa > > │
├┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┤
│ ) │ Balanced () │ !( *a (bb) )! │ ! aa (bb) ! │ (( aa (bb) )) │
│ ] │ Balanced [] │ ![ *a [bb] ]! │ ! aa [bb] ! │ [[ aa [bb] ]] │
│ } │ Balanced {} │ !{ *a {bb} }! │ ! aa {bb} ! │ {{ aa {bb} }} │
│ > │ Balanced <> │ !< *a >! │ ! aa ! │ << aa >> │
│ b │ Alias for │ !( *a {bb} )! │ ! aa {bb} ! │ (( aa {bb} )) │
│ │ ), ], or } │ │ │ │
├┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┤
│ q │ Alias for │ !'aa'*a'aa'! │ !'aaaaaa'! │ "'aa'aa'aa'" │
│ │ ", ', or ` │ │ │ │
├┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┤
│ ? │ User prompt │ !e * o! │ ! a ! │ ee a oo │
│ │(typed e and o)│ │ │ │
├┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┤
│ t │ Tag │ !*! │ !a! │ a │
│ │ │ │ │ (typed y) │
├┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┤
│ f │ Function call │ !f(*a, bb)! │ !aa, bb! │ g(f(*a, bb)) │
│ │ │ │ │ (typed g) │
├┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┤
│ │ Default │ !_a*a_! │ !aaa! │ __aaa__ │
│ │ (typed _) │ │ │ │
└┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┘
<
Notes:
- All examples assume default `config.search_method`.
- Open brackets differ from close brackets by how they treat inner edge
whitespace: open includes it left and right parts, close does not.
- Output value of `b` alias is same as `)`. For `q` alias - same as `"`.
- Default surrounding is activated for all characters which are not
configured surrounding identifiers. Notes:
- Due to special handling of underlying `x.-x` Lua pattern
(see |MiniSurround-search-algorithm|), it doesn't really support
non-trivial `[count]` for "cover" search method.
- When cursor is exactly on the identifier character while there are
two matching candidates on both left and right, the one resulting in
region with smaller width is preferred.
------------------------------------------------------------------------------
*MiniSurround-glossary*
Note: this is similar to |MiniAi-glossary|.
REGION ~
Table representing region in a buffer. Fields: and for
inclusive start and end positions ( might be `nil` to describe empty
region). Each position is also a table with line and column
(both start at 1). Examples: >lua
{ from = { line = 1, col = 1 }, to = { line = 2, col = 1 } }
-- Empty region
{ from = { line = 10, col = 10 } }
<
REGION PAIR ~
Table representing regions for left and right surroundings. Fields:
and with regions. Example: >lua
{
left = { from = { line = 1, col = 1 }, to = { line = 1, col = 1 } },
right = { from = { line = 1, col = 3 } },
}
<
PATTERN ~
String describing Lua pattern.
SPAN ~
Interval inside a string (end-exclusive). Like [1, 5). Equal `from` and `to` edges
describe empty span at that point.
Span `A = [a1, a2)` covers `B = [b1, b2)` if every element of `B` is within
`A` (`a1 <= b < a2`). It also is described as "B is nested inside A".
NESTED PATTERN ~
Array of patterns aimed to describe nested spans.
SPAN MATCHES NESTED PATTERN ~
If there is a sequence of consecutively nested spans each matching
corresponding pattern within substring of previous span (or input string
for first span). Example: >lua
-- Nested patterns for balanced `()` with inner space
{ '%b()', '^. .* .$' }
-- Example input string (with columns underneath for easier reading):
"( ( () ( ) ) )"
-- 12345678901234
<
Here are all matching spans [1, 15) and [3, 13). Both [5, 7) and [8, 10)
match first pattern but not second. All other combinations of `(` and `)`
don't match first pattern (not balanced).
COMPOSED PATTERN ~
Array with each element describing possible pattern (or array of them) at
that place. Composed pattern basically defines all possible combinations of
nested pattern (their cartesian product). Examples:
1. Either balanced `()` or balanced `[]` but both with inner edge space: >lua
-- Composed pattern
{ { '%b()', '%b[]' }, '^. .* .$' }
-- Composed pattern expanded into equivalent array of nested patterns
{ '%b()', '^. .* .$' } -- and
{ '%b[]', '^. .* .$' }
<
2. Either "balanced `()` with inner edge space" or "balanced `[]` with no
inner edge space", both with 5 or more characters: >lua
-- Composed pattern
{ { { '%b()', '^. .* .$' }, { '%b[]', '^.[^ ].*[^ ].$' } }, '.....' }
-- Composed pattern expanded into equivalent array of nested patterns
{ '%b()', '^. .* .$', '.....' } -- and
{ '%b[]', '^.[^ ].*[^ ].$', '.....' }
<
SPAN MATCHES COMPOSED PATTERN ~
If it matches at least one nested pattern from expanded composed pattern.
------------------------------------------------------------------------------
*MiniSurround-surrounding-specification*
Surround specification is a table with keys:
- - defines how to find and extract surrounding for "input"
operations (like `delete`). See more in "Input surrounding" section.
-