-
-
Notifications
You must be signed in to change notification settings - Fork 63
Configuration
Warning: Everything on this page is specific to the pattern-matching
branch, and
will not work with the main branch!
To configure nvim-surround
, simply call require("nvim-surround").setup
with
your desired configuration. This sets up your "global configuration" for the
plugin, which is enabled on all buffers.
To configure buffer-local behavior, call
require("nvim-surround").buffer_setup
. One common use case is to run this
command in ftplugin/[language].lua
, to have a custom setup per filetype.
Configuration for nvim-surround
is additive, meaning that any configuration
you provide will be merged with the default, and the result is used. This means
that it is unnecessary to re-define all defaults in your configuration if you
just want to add one or two things. In particular, when you call
require("nvim-surround").setup
the provided configuration overrides the
default configuration where there are conflicts, and uses them otherwise.
Furthermore, when you call require("nvim-surround").buffer_setup
the provided
configuration overrides your global configuration where there are conflicts, and
uses them otherwise.
A selection is a table with two keys first_pos
and last_pos
, each of which
is a pair of integers representing a line number/column pair, inclusive. For
example:
local selection = {
first_pos = { 1, 1 },
last_pos = { 10, 5 },
}
A pair of selections is a table with two keys left
and right
, each of
which is a selection, inclusive. For example:
local selections = {
left = {
first_pos = { 1, 1 },
last_pos = { 1, 3 },
},
right = {
first_pos = { 5, 6 },
last_pos = { 6, 3 },
},
}
The default
configuration
gives a list of the provided defaults. To override any of the given defaults,
replace the corresponding value. To disable any of the provided defaults, set
the corresponding value to false
. For example, the following configuration
disables b
and B
as aliases, and uses them to add LaTeX-style braces instead
of their defaults.
-- Put this in ftplugin/tex.lua
require("nvim-surround").buffer_setup({
delimiters = {
["b"] = {
add = { "\\left(", "\\right)" },
},
["B"] = {
add = { "\\left{", "\\right}" },
},
},
aliases = {
["b"] = false,
["B"] = false,
},
})
The values of this table are strings or false
. If a string, then that string
is the keymap used to trigger a certain action. If false
, then the keymap is
disabled.
This table contains the definitions for all the various surround actions. Each key must be a string of exactly one character (with one exception), and maps to a table with the following three (optional) keys.
The one exception to this rule is the key invalid_key_behavior
, which defines
what behavior is exhibited when a key doesn't exist, i.e. throw an error, do
nothing, or something else entirely. TODO: Find a better place to explain this.
The value of this key is a function that returns the delimiter pair that is used
when adding a new surround to the buffer (via insert
/normal
/visual
or any
of their variants).
Note: nvim-surround
uses the idea that tables of strings denote multi-line
strings. Delimiters are pairs of tables of strings, with the first element
denoting the left delimiter, and the second element denoting the right.
If the delimiter pairs to be added are static, then the function can be omitted
and the add
key can directly take on the value of the table. Furthermore,
delimiters that are just a single line can be represented by a string, instead
of a table with one element. For example, the following configurations are
equivalent:
-- "Standard" form
require("nvim-surround").buffer_setup({
delimiters = {
["*"] = {
add = function()
return { { "multi", "line", "string" }, { "single-line string" } },
end,
},
},
})
-- Using syntactic sugar
require("nvim-surround").buffer_setup({
delimiters = {
["*"] = {
add = { { "multi", "line", "string" }, "single-line string" },
},
},
})
While these delimiter pairs can be static, anything can be run in these functions. User input, the current buffer's contents, and even Tree-sitter queries are all fair game to be used when determining the delimiter pair.
The value of the find
key is either a function that returns a selection
or a string that is interpreted as a Lua
pattern, which is used to
find the selection that includes the delimiter pair to be modified. If it is
omitted, then nvim-surround
defaults to using the a[char]
text-object to
find the selection. For example, when modifying parentheses, the a)
text-object already exists, so the find
key is omitted.
The value of this optional key is a function that returns a pair of selections, or a string that is interpreted as a Lua pattern, which is used to narrow down the found selection to the selections to be deleted. The way this is done is via match groups, where match groups determine what the left and right delimiters are. However, due to quirks of the Lua language, four match groups are needed:
- The match group containing the left delimiter
- An empty match group
()
immediately following the first group - The match group containing the right delimiter
- An empty match group
()
immediately following the second group
If this key is omitted, then the delimiter pair is assumed to exist at the ends
of the selection, and the length of the pair given in the add
key is used to
discern the selections. For example, the ]
surround omits the delete
key,
the length of both [
and ]
is 1, so the first and last characters get
deleted.
The change
key holds a table that contains two keys, target
and
replacement
. If this key is omitted, then the delimiter pair is assumed to
exist at the ends of the selection, and is deleted. The user is then queried for
a second character, which is used to add a new delimiter pair at the old
locations.
Like delete
, the value of this key is a function, or string that is
interpreted as a Lua
pattern, and is used to
narrow down the found selection to the selections to be changed. Again, you must
provide all four match groups, two for the selections and two empty groups. This
is explicitly a different string from the delete
key, since it is often the
case that deleting and changing surrounding pairs affect two different
selections. For example, deleting a function call includes the parentheses, but
changing (renaming) it does not.
Like add
, the value of this optional key is a function that returns the
delimiter pair that is used when adding a new surround to the buffer. If this
key is provided, then the user is not queried for another character, and this
function is used to replace the deleted delimiters. If it is omitted, then the
add
key for a user-queried character is used to provide the replacement.
TODO
TODO
Consider the default configuration for HTML tags via the t
key (the only
difference with T
is how changing tags is handled):
["t"] = {
add = function()
local input = get_input("Enter the HTML tag: ")
if input then
local element = input:match("^<?([%w-]+)")
local attributes = input:match("%s+([^>]+)>?$")
if not element then
return nil
end
local open = attributes and element .. " " .. attributes or element
local close = element
return { { "<" .. open .. ">" }, { "</" .. close .. ">" } }
end
end,
delete = "^(%b<>)().-(%b<>)()$",
change = {
target = "^<([%w-]*)().-([^/]*)()>$",
replacement = function()
local element = get_input("Enter the HTML element: ")
if element then
return { { element }, { element } }
end
end,
},
},
After the user triggers the add
function, the following occurs:
- The user is queried for some input
- The input is split into its element and attributes
- The opening tag is created using both the element and attributes, whereas the closing tag only uses the element type
- A table is returned that contains the opening/closing tag, with the corresponding angle brackets
For example:
- The user triggers the
add
function, viainsert
/normal
/visual
keymaps (or any of their variants) - The user inputs
div id="some div"
and hits<CR>
- The input string is split such that the element contains
div
, and the attributes areid="some div"
- The open/closing tags are created and returned,
<div id="some div">
and</div>
TODO: delete
key should accept functions as well
When the user types dst
, the following occurs:
-
nvim-surround
tries to use thefind
key to find the surrounding HTML selection, but none exists- It instead defaults to the
at
text-object, and properly finds the nearest tag selection - Note: This method is preferred to pattern matching, as it properly deals with nested HTML tags
- It instead defaults to the
-
nvim-surround
uses thedelete
key, and since it is a string, interprets it as a Lua pattern- In this case, the Lua pattern matches the outermost set of HTML tags
- The selections get deleted
For example, consider the following buffer, where the cursor lays on the placeholder text:
<div id="some id"
class="some class">
Some placeholder text.
<h1>
Hello world!
</h1>
</div>
-
nvim-surround
uses theat
text-object to get the surrounding HTML selection, which in this case happens to be the entire buffer - The Lua pattern in the
delete
key has the value^(%b<>)().-(%b<>)()$
- The left delimiter is the opening HTML tag, and the right delimiter the closing tag
- Empty capture groups are provided immediately after both capture groups
- The outer
div
tag gets deleted
TODO: change.target
key should accept functions as well
When the user types cst
, the following occurs:
-
nvim-surround
tries to use thefind
key to find the surrounding HTML selection, but none exists- It instead defaults to the
at
text-object, and properly finds the nearest tag selection - Note: This method is preferred to pattern matching, as it properly deals with nested HTML tags
- It instead defaults to the
-
nvim-surround
uses thechange.target
key, and since it is a string, interprets it as a Lua pattern- In this case, the Lua pattern matches the tag type of the outer
div
tag
- In this case, the Lua pattern matches the tag type of the outer
- The selections get deleted, and
nvim-surround
checks if achange.replacement
key exists- Since one does, the user is not queried for another input character, and
the
change.replacement
function is used to replace the deleted selections
- Since one does, the user is not queried for another input character, and
the
Consider the same sample buffer from before, with the cursor again residing on the placeholder text:
<div id="some id"
class="some class">
Some placeholder text.
<h1>
Hello world!
</h1>
</div>
-
nvim-surround
uses theat
text-object to get the surrounding HTML selection, which in this case happens to be the entire buffer - The Lua pattern in the
change.target
key has the value^<([%w-]*)().-([^/]*)()>$
, which selects only the tag type- In this case, just the word
div
on the first and last line are selected - Empty capture groups are provided immediately after both capture groups
- In this case, just the word
- The
div
s get deleted andchange.replacement
is called, which queries the user for replacement text- If the user types
h2
and then hits<CR>
, then thediv
gets replaced byh2
, yielding the following buffer:
- If the user types
<h2 id="some id"
class="some class">
Some placeholder text.
<h1>
Hello world!
</h1>
</h2>
Consider the default configuration for function calls via the f
key:
["f"] = {
add = function()
local result = get_input("Enter the function name: ")
if result then
return { { result .. "(" }, { ")" } }
end
end,
find = "[%w_]+%b()",
delete = "^([%w_]+%()().-(%))()$",
change = {
target = "^([%w_]+)().-()()$",
replacement = function()
local result = get_input("Enter the function name: ")
if result then
return { { result }, { "" } }
end
end,
},
},
After the user triggers the add
function, the following occurs:
- The user is queried for the function name
- The left delimiter ends up being
[function name](
, and the right delimiter)
Consider the following buffer, where the cursor is over the word args
. If the
user types ysiwf
, and then func_name
for the function name, the buffer
local eval = args
would get transformed into
local eval = func_name(args)
When the user types dsf
, the following occurs:
-
nvim-surround
uses thefind
key to find the nearest function call - The
delete
key is then used to match the function name and opening parenthesis, as well as the closing parenthesis - The function call is deleted
Consider the following buffer, where the cursor resides on some_args
:
local eval = func_1(func_2(some_args))
When dsf
is typed, the following occurs:
-
nvim-surround
uses the find key[%w_]+%b()
to find the selection, which in this case is the inner function callfunc_2(some_args)
- The
delete
key^([%w_]+%()().-(%))()$
matches the delimiter pair tofunc_2(
and)
, which then gets deleted
The resulting buffer is
local eval = func_1(some_args)
When the user types csf
, the following occurs:
-
nvim-surround
uses thefind
key to find the nearest function call - The
change.target
key is then used to match the function name, which is then deleted - Since a
change.replacement
key is provided, the user is not queried for another character, andchange.replacement
is evaluated to see what pair should replace the deleted pair - In this case, the operation acts to just rename the function
Consider the same buffer, but the cursor now resides on eval
:
local eval = func_1(func_2(some_args))
When csf
is typed, the following occurs:
-
nvim-surround
uses the find key[%w_]+%b()
to find the selection, which in this case is the outer function callfunc_1(func_2(some_args))
- The
change.target
key^([%w_]+)().-()()$
matches the delimiter pair tofunc_1
- Note that since we only wish to replace the function name, we only provide a match group for the left selection to change
- It is still necessary to provide all four capture groups; we simply let the third one be empty in place of capturing the right selection
- Since the
change.replacement
key exists, the user is not queried for another input character, and thechange.replacement
function is evaluated for the replacement text- If the user types
a_new_name
and hits<CR>
, then the function gets renamed toa_new_name
, yielding the following buffer:
- If the user types
local eval = a_new_name(func_2(some_args))