Surrounds Showcase #53
Replies: 13 comments 24 replies
-
Custom function surrounds per code block in a Markdown filetreesitter_demo_1.mp4Implementationlocal surr_utils = require("nvim-surround.config")
local ts_utils = require("nvim-treesitter.ts_utils")
local query = vim.treesitter.query
require("nvim-surround").buffer_setup({
surrounds = {
["f"] = {
add = function()
local cur = ts_utils.get_node_at_cursor(0, true)
while cur and cur:type() ~= "fenced_code_block" do
cur = cur:parent()
end
local language = nil
if cur then
for child_node in cur:iter_children() do
if child_node:type() == "info_string" then
language = query.get_node_text(child_node:child(0), 0)
end
end
end
if language == "cpp" then
local input = surr_utils.get_input("Enter a function name: ")
if input then
return {
{ "auto " .. input .. " = [](" },
{
"){",
" return 0;",
"}",
},
}
end
elseif language == "lua" then
local input = surr_utils.get_input("Enter a function name: ")
if input then
return {
{ "local " .. input .. " = function(" },
{
")",
" return nil",
"end",
},
}
end
elseif language == "python" then
local input = surr_utils.get_input("Enter a function name: ")
if input then
return {
{ input .. " = lambda " },
{
":",
" return NULL",
},
}
end
end
return { { "" }, { "" } }
end,
},
},
}) |
Beta Was this translation helpful? Give feedback.
-
Surround markdown link title, using clipboard contents@andrewferrier I got inspired by your Markdown surround, and came up with this instead markdown_link_demo.mp4Implementationrequire("nvim-surround").buffer_setup({
surrounds = {
["l"] = {
add = function()
local clipboard = vim.fn.getreg("+"):gsub("\n", "")
return {
{ "[" },
{ "](" .. clipboard .. ")" },
}
end,
find = "%b[]%b()",
delete = "^(%[)().-(%]%b())()$",
change = {
target = "^()()%b[]%((.-)()%)$",
replacement = function()
local clipboard = vim.fn.getreg("+"):gsub("\n", "")
return {
{ "" },
{ clipboard },
}
end,
},
},
},
}) |
Beta Was this translation helpful? Give feedback.
-
Basic LaTeXThis isn't doing anything complex enough to be interesting, but it was the first thing I wanted. % Before
Foo
% After command: ysiwctextit
\textit{Foo}
% Before
Bar
% After command: VSecentering
\begin{centering}
Bar
\end{centering} Note that this implementation only adds commands and environments; the excellent Implementationrequire("nvim-surround").buffer_setup {
surrounds = {
["c"] = {
add = function()
local cmd = require("nvim-surround.config").get_input "Command: "
return { { "\\" .. cmd .. "{" }, { "}" } }
end,
},
["e"] = {
add = function()
local env = require("nvim-surround.config").get_input "Environment: "
return { { "\\begin{" .. env .. "}" }, { "\\end{" .. env .. "}" } }
end,
},
},
} |
Beta Was this translation helpful? Give feedback.
-
Print StatementsVery often I find myself writing print debug statements, but find it very tedious to add them in manually when I've already copy-pasted what I want to print from the line above. With this surround and -- Before modification
arg1, arg2
-- Type `yssp`
vim.pretty_print(arg1, arg2)
-- Type `dsp`
arg1, arg2 Edit: C++ demo below, it takes into account whether or not your file includes cpp_print_demo.mp4Implementation-- ftplugin/lua.lua
require("nvim-surround").buffer_setup({
surrounds = {
["p"] = {
add = { "vim.pretty_print(", ")" },
find = "vim%.pretty_print%b()",
delete = "^(vim%.pretty_print%()().-(%))()$",
change = {
target = "^(vim%.pretty_print%()().-(%))()$",
},
},
}
})
-- ftplugin/cpp.lua
require("nvim-surround").buffer_setup({
surrounds = {
["p"] = {
add = function()
for _, line in ipairs(vim.api.nvim_buf_get_lines(0, 0, -1, false)) do
if line == "using namespace std;" then
return { { "cout << " }, { " << endl;" } }
end
end
return { { "std::cout << " }, { " << std::endl;" } }
end,
find = function()
local config = require("nvim-surround.config")
for _, line in ipairs(vim.api.nvim_buf_get_lines(0, 0, -1, false)) do
if line == "using namespace std;" then
return config.get_selection({ pattern = "cout << .- << endl;" })
end
end
return config.get_selection({ pattern = "std::cout << .- << std::endl;" })
end,
delete = function()
local config = require("nvim-surround.config")
for _, line in ipairs(vim.api.nvim_buf_get_lines(0, 0, -1, false)) do
if line == "using namespace std;" then
return config.get_selections({
char = "p",
pattern = "^(cout << )().-( << endl;)()$",
})
end
end
return config.get_selections({
char = "p",
pattern = "^(std::cout << )().-( << std::endl;)()$",
})
end,
},
},
}) |
Beta Was this translation helpful? Give feedback.
-
Object KeysMany languages (Python, R, and Lua, for example) feature complex data structures that consist of elements that can be accessed by name using this syntax:
Often, I find I want to turn an object in the global environment into an element of a containing object, or vice versa. This mapping provides a simple way to do that. It prompts the user for the name of the containing object, then turns the text object into an element reference for it. Deletion removes everything but the element name. Right now it won't work for a key that uses quote marks, (e.g., Edited to cover element references from object names (e.g., Implementationrequire("nvim-surround").setup({
surrounds = {
["k"] = {
add = quoted_surround,
find = quoted_pattern,
delete = quoted_pattern,
change = {
target = quoted_pattern,
},
},
["K"] = {
add = unquoted_surround,
find = unquoted_pattern,
delete = unquoted_pattern,
change = {
target = unquoted_pattern,
},
},
},
})
|
Beta Was this translation helpful? Give feedback.
-
Delete Surrounding FunctionSo after seeing the example of surrounding a function, I thought why not write a custom surround deletion that removes the surrounding function syntax. Should be useful for refactoring. Implemented for lua, but you can add more languages by adding filetype-specific lua patterns for -- Before modification
function demo()
print("test")
end
-- Type `dsf`
print("test") Implementation-- requires treesitter-textobj plugin & treesitter
require("nvim-treesitter.configs").setup {
textobjects = {
select = {
keymaps = {
["af"] = "@function.outer",
},
}
}
}
require("nvim-surround").setup {
surrounds = {
["f"] = {
find = function()
return require("nvim-surround.config").get_selection {motion = "af"}
end,
delete = function()
local ft = bo.filetype
local patt
if ft == "lua" then
patt = "^(.-function.-%b())().*(end)()$"
else
vim.notify("No function-surround defined for " .. ft)
patt = "()()()()"
end
return require("nvim-surround.config").get_selections {
char = "f",
pattern = patt,
}
end,
}
} |
Beta Was this translation helpful? Give feedback.
-
LaTeX commands and environmentsThis implementation builds on @swarn 's, supporting replacement and deletion for LaTeX commands and environments. It supports options (e.g., (I know lervag/vimtex already provides The mnemonic is:
Note that some commands (e.g., Here's just some basic examples that summarizes how I use the changing and deletion commands: \mycmd{change the surrounding command to something else, then delete it}
% cscothercmd<CR>
\othercmd{change the surrounding command to something else, then delete it}
% dsc
change the surrounding command to something else, then delete it
\mycmd[myopts]{change the surrounding command and options to something else, then delete them}
% csCothercmd<CR>otheropts<CR>
\othercmd[otheropts]{change the surrounding command and options to something else, then delete them}
% dsC
change the surrounding command and options to something else, then delete them
\begin{myenv}[myopts]
Change the surrounding environment
Change the surrounding options
Delete the surrounding environment and its options
\end{myenv}
% cseotherenv<CR>
\begin{otherenv}[myopts]
Change the surrounding environment
Change the surrounding options
Delete the surrounding environment and its options
\end{otherenv}
% csEotheropts<CR>
\begin{otherenv}[otheropts]
Change the surrounding environment
Change the surrounding options
Delete the surrounding environment and its options
\end{otherenv}
% dsE
Change the surrounding environment
Change the surrounding options
Delete the surrounding environment and its options
Options are actually kind of fussy to handle, so there's some quirks and inconsistencies between commands and environments when it comes to dealing with options:
I documented these edge cases in the following examples: More commands examplessurround-this-with-command
% ysiWcmycmd<CR>
\mycmd{surround-this-with-command}
surround-this-with-command-and-options
% ysiWCmycmd<CR>myopts<CR>
\mycmd[myopts]{surround-this-with-command-and-options}
\mycmd{change the surrounding command to something else, then delete it}
% cscothercmd<CR>
\othercmd{change the surrounding command to something else, then delete it}
% dsc
change the surrounding command to something else, then delete it
\mycmd[myopts]{change the surrounding command and options to something else, then delete them}
% csCothercmd<CR>otheropts<CR>
\othercmd[otheropts]{change the surrounding command and options to something else, then delete them}
% dsC
change the surrounding command and options to something else, then delete them
\outer[opts]{\inner{change the outer (with opts), delete the inner (no opts)}}
% csCmycmd<CR>myopts<CR>
\mycmd[myopts]{\inner{change the outer (with opts), delete the inner (no opts)}}
% dsc
\mycmd[myopts]{change the outer (with opts), delete the inner (no opts)}
\outer[opts]{\inner{change the inner (no opts), delete the outer (with opts)}}
% cscmycmd<CR>
\outer[opts]{\mycmd{change the inner (no opts), delete the outer (with opts)}}
% dsC
\mycmd{change the inner (no opts), delete the outer (with opts)}
\outer{\inner[opts]{change the outer (no opts), delete the inner (with opts)}}
% cscmycmd<CR>
\mycmd{\inner[opts]{change the outer (no opts), delete the inner (with opts)}}
% dsC
\mycmd{change the outer (no opts), delete the inner (with opts)}
\outer{\inner[opts]{change the inner (with opts), delete the outer (no opts)}}
% csCmycmd<CR>myopts<CR>
\outer{\mycmd[myopts]{change the inner (with opts), delete the outer (no opts)}}
% dsc
\mycmd[myopts]{change the inner (with opts), delete the outer (no opts)} More environments examplessurround-this-with-environment
% ysiWemyenv<CR>
\begin{myenv}surround-this-with-environment\end{myenv}
surround-this-with-environment-and-options
% ysiWEmyenv<CR>myopts<CR>
\begin{myenv}[myopts]surround-this-with-environment-and-options\end{myenv}
\begin{myenv}
Change the surrounding environment to something else, then delete it
\end{myenv}
% cseotherenv<CR>
\begin{otherenv}
Change the surrounding environment to something else, then delete it
\end{otherenv}
% dse
Change the surrounding environment to something else, then delete it
\begin{myenv}[opts]
When the surrounding environment has options:
- cse only changes the environment
- dse does not delete the options (!!), only begin-end pair
\end{myenv}
% cseotherenv<CR>
\begin{otherenv}[opts]
When the surrounding environment has options:
- cse only changes the environment
- dse does not delete the options (!!), only begin-end pair
\end{otherenv}
% dse
[opts]
When the surrounding environment has options:
- cse only changes the environment
- dse does not delete the options (!!), only begin-end pair
\begin{myenv}[opts]
When the surrounding environment has options:
- csE only changes the options
- dsE deletes both begin-end pair and its options
\end{myenv}
% csEmyopts<CR>
\begin{myenv}[myopts]
When the surrounding environment has options:
- csE only changes the options
- dsE deletes both begin-end pair and its options
\end{myenv}
% dsE
When the surrounding environment has options:
- csE only changes the options
- dsE deletes both begin-end pair and its options
\begin{myenv}
When the surrounding environment does not have options,
csE and dsE do not do anything
\end{myenv}
% csE
\begin{myenv}
When the surrounding environment does not have options,
csE and dsE do not do anything
\end{myenv}
% dsE
\begin{myenv}
When the surrounding environment does not have options,
csE and dsE do not do anything
\end{myenv} Implementationlocal function tex_find_environment()
local cfg = require("nvim-surround.config")
if vim.g.loaded_nvim_treesitter then
local selection = cfg.get_selection {
node = "generic_environment",
-- query = {
-- capture = "@block.outer",
-- type = "textobjects",
-- }
-- NOTE: ^query doesn't seem to work very reliably with LaTeX environments
}
if selection then
return selection
end
end
return cfg.get_selection [[\begin%b{}.-\end%b{}]]
-- NOTE: ^this does not correctly handle \begin{}-\end{} pairs in all cases
-- (hence we use treesitter if available)
end
local tex_opts = {
surrounds = {
["c"] = {
add = function()
local cfg = require("nvim-surround.config")
local cmd = cfg.get_input("Command: ")
return { { "\\" .. cmd .. "{" }, { "}" } }
end,
find = [=[\[^\{}%[%]]-%b{}]=],
delete = [[^(\[^\{}]-{)().-(})()$]],
change = {
target = [[^\([^\{}]-)()%b{}()()$]],
replacement = function()
local cfg = require("nvim-surround.config")
local cmd = cfg.get_input("Command: ")
return { { cmd }, { "" } }
end
},
},
["C"] = {
add = function()
local cfg = require("nvim-surround.config")
local cmd, opts = cfg.get_input("Command: "), cfg.get_input("Options: ")
return { { "\\" .. cmd .. "[" .. opts .. "]{" }, { "}" } }
end,
find = [[\[^\{}]-%b[]%b{}]],
delete = [[^(\[^\{}]-%b[]{)().-(})()$]],
change = {
target = [[^\([^\{}]-)()%[(.*)()%]%b{}$]],
replacement = function()
local cfg = require("nvim-surround.config")
local cmd, opts = cfg.get_input("Command: "), cfg.get_input("Options: ")
return { { cmd }, { opts } }
end
},
},
["e"] = {
add = function()
local cfg = require("nvim-surround.config")
local env = cfg.get_input("Environment: ")
return { { "\\begin{" .. env .. "}" }, { "\\end{" .. env .. "}" } }
end,
find = tex_find_environment,
delete = [[^(\begin%b{})().*(\end%b{})()$]],
change = {
target = [[^\begin{(.-)()%}.*\end{(.-)()}$]],
replacement = function()
local env = require("nvim-surround.config").get_input("Environment: ")
return { { env }, { env } }
end,
}
},
["E"] = {
add = function()
local cfg = require("nvim-surround.config")
local env, opts = cfg.get_input("Environment: "), cfg.get_input("Options: ")
return { { "\\begin{" .. env .. "}[" .. opts .. "]" }, { "\\end{" .. env .. "}" } }
end,
find = tex_find_environment,
delete = [[^(\begin%b{}%b[])().*(\end%b{})()$]],
change = {
target = [[^\begin%b{}%[(.-)()()()%].*\end%b{}$]],
replacement = function()
local cfg = require("nvim-surround.config")
local env = cfg.get_input("Environment options: ")
return { { env }, { "" } }
end,
}
},
},
}
-- Key is filetype, value is the options table to be passed to require("nvim-surround").buffer_setup()
local ft_opts = {
tex = tex_opts,
plaintex = tex_opts,
} |
Beta Was this translation helpful? Give feedback.
-
JavaScript String Interpolation WrapperThis surround supports adding interpolation surrounds to words/visual selections and swaps the surrounding string to a template string if necessary. Example: Change surrounding string and interpolate current word console.log('some string with a variable');
console.log("some string with a variable"); while hovering the word console.log(`some string with a ${variable}`); This will also work if the string is already wrapped with the If the word is not wrapped in a string at all, it will wrap the immediate word with both so, this: console.log(variable); would become this: console.log(`${variable}`); Planning on extending to also delete interpolated variables and also (maybe?) swap back to a regular string at that point, but this is what I'm finding useful right now and wanted to share. Implementationsurrounds = {
["s"] = {
add = function()
local ts_utils = require("nvim-treesitter.ts_utils")
local cur = ts_utils.get_node_at_cursor(0, true)
local language = vim.bo.filetype
local is_jsy = (
language == "javascript"
or language == "javascriptreact"
or language == "typescript"
or language == "typescriptreact"
)
if is_jsy then
local cur_type = cur:type()
local interpolation_surround = { { "${" }, { "}" } }
if cur and (cur_type == "string" or cur_type == "string_fragment") then
vim.cmd.normal("csq`")
return interpolation_surround
elseif cur and cur_type == "template_string" then
return interpolation_surround
else
return { { "`${" }, { "}`" } }
end
end
end,
},
}, |
Beta Was this translation helpful? Give feedback.
-
Markdown Code BlocksVery simple one to surround text with markdown triple-backticks. Prompts you for the code language, or defaults to empty. E.g. println!("foo") to: ```rust
println!("foo")
``` Implementation copied from #88 , adding here just because I looked for it here. Mapped to Implementation: -- Surround with markdown code block, triple backticks.
-- <https://github.com/kylechui/nvim-surround/issues/88>
["~"] = {
add = function()
local config = require("nvim-surround.config")
local result = config.get_input("Markdown code block language: ")
return {
{ "```\n" .. result, '' },
{ "", "```" },
}
end,
}, |
Beta Was this translation helpful? Give feedback.
-
GenericsSurround content with a generic type: Before: Implementation (basically copied from the function built-in) surrounds = {
-- "generic"
g = {
add = function()
local config = require("nvim-surround.config")
local result = config.get_input("Enter the generic name: ")
if result then
return { { result .. "<" }, { ">" } }
end
end,
find = function()
local config = require("nvim-surround.config")
return config.get_selection({ node = "generic_type" })
end,
delete = "^(.-<)().-(>)()$",
change = {
target = "^(.-<)().-(>)()$",
replacement = function()
local config = require("nvim-surround.config")
local result = config.get_input("Enter the generic name: ")
if result then
return { { result .. "<" }, { ">" } }
end
end,
}
},
},
|
Beta Was this translation helpful? Give feedback.
-
Spanish charactersSpanish language makes use of opening and closing punctuation characters, e.g: Quotation marks
Exclamation marks
Question marks
Implementationsurrounds = {
["s"] = {
add = function()
return {
{ "«" },
{ "»" },
}
end,
},
["e"] = {
add = function()
return {
{ "¡" },
{ "!" },
}
end,
},
["q"] = {
add = function()
return {
{ "¿" },
{ "?" },
}
end,
}
} |
Beta Was this translation helpful? Give feedback.
-
Better code blocksThis is a little better implementation of code blocks. It allows to modify text before the Create function from existing code document.addEventListener("mousemove", handleMouseMove);
document.addEventListener("mouseup", handleMouseUp); <-- cursor here
transforms to function registerEventListeners() {
document.addEventListener("mousemove", handleMouseMove);
document.addEventListener("mouseup", handleMouseUp);
} If you change your mind and want to modify the name of the new function, you can use
function createEventListeners() {
document.addEventListener("mousemove", handleMouseMove);
document.addEventListener("mouseup", handleMouseUp);
} and finally you can quickly remove function
document.addEventListener("mousemove", handleMouseMove);
document.addEventListener("mouseup", handleMouseUp); It works the same with conditional blocks so you can quickly create / modify / remove Implementation b = {
add = function()
local config = require("nvim-surround.config")
local result = config.get_input("Enter the block prefix: ")
if result then
return { { result .. "{" }, { "}" } }
end
end,
find = function()
local config = require("nvim-surround.config")
return config.get_selection({
query = {
capture = "@block.outer",
type = "textobjects",
},
})
end,
delete = "^(.-{)().-(})()$",
change = {
target = "^(.-{)().-(})()$",
replacement = function()
local config = require("nvim-surround.config")
local result = config.get_input("Enter the block prefix: ")
if result then
return { { result .. "{" }, { "}" } }
end
end,
},
}, |
Beta Was this translation helpful? Give feedback.
-
HTML: Emmet AbbreviationWrap a text object in an emmet abbreviation <p>put the emphasis on the right syllable</p>
^
<p>put the <em class="jazzy">emphasis</em> on the right syllable</p>
^ There's room for improvement here, namely: when emmet LSP provides completion stops (e.g. surround a word with Implementationt = {
-- add = wrap_with_abbreviation,
add = function()
local input = vim.fn.input 'Emmet Abbreviation:'
if input then
--- hat_tip to https://github.com/b0o/nvim-conf/blob/363e126f6ae3dff5f190680841e790467a00124d/lua/user/util/wrap.lua#L12
local bufnr = 0
local client = unpack(vim.lsp.get_clients{ bufnr = bufnr, name = 'emmet_language_server' })
if client then
local splitter = 'BENNYSPECIALSECRETSTRING'
local response = client.request_sync('emmet/expandAbbreviation', {
abbreviation = input,
language = vim.opt.filetype,
options = {
text = splitter,
},
}, 50, bufnr)
if response then
if response.err then
vim.notify(response.err.message)
else
return (vim.split(response.result, splitter))
end
end
end
end
end,
find = function()
return require 'nvim-surround.config'.get_selection { motion = 'at' }
end,
delete = '^(%b<>)().-(%b<>)()$',
change = {
-- TODO: this is cribbed from the original impl
-- but doesn't yet actually call emmet
target = '^<([^%s<>]*)().-([^/]*)()>$',
replacement = function()
local input = vim.fn.input'New Emmet Abbreviation:'
if input then
local element = input:match '^<?([^%s>]*)'
local attributes = input:match '^<?[^%s>]*%s+(.-)>?$'
local open = attributes and element .. ' ' .. attributes or element
local close = element
return { { open }, { close } }
end
end,
},
}, |
Beta Was this translation helpful? Give feedback.
-
Hey everybody! With the release of function-evaluated surrounds, I thought I might create a thread where people can share custom surrounds that they think others might benefit from. If this goes well, I might also create a page in the Wiki for the best ones. If you'd like to contribute, I ask that you please format your responses as a GIF/video embed that shows off your surround, with the corresponding setup code hidden inside a
details
drop down menu. Thanks to everybody that makes a submission!Submission Template
Beta Was this translation helpful? Give feedback.
All reactions