Top Related Projects
surround.vim: Delete/change/add parentheses/quotes/XML-tags/much more with ease
Add/change/delete surrounding delimiter pairs with ease. Written with :heart: in Lua.
repeat.vim: enable repeating supported plugin maps with "."
Quick Overview
vim-sandwich is a Vim plugin that provides text objects and operators for adding, deleting, and replacing surroundings like parentheses, quotes, and HTML tags. It enhances Vim's text manipulation capabilities, making it easier to work with paired characters and tags in various file types.
Pros
- Highly customizable with support for user-defined surroundings
- Intuitive and consistent keybindings that follow Vim's conventions
- Works seamlessly with Vim's repeat (.) and undo (u) commands
- Supports both normal mode operations and text objects for visual mode
Cons
- Learning curve for users unfamiliar with Vim's text object concept
- May conflict with other plugins that modify similar functionalities
- Some advanced features require additional configuration
Code Examples
- Adding surroundings:
" Surround a word with parentheses
ysiw)
" Surround a line with HTML tags
yss<p>
- Deleting surroundings:
" Delete surrounding quotes
ds"
" Delete surrounding HTML tags
dst
- Replacing surroundings:
" Replace parentheses with square brackets
cs([
" Replace quotes with angle brackets
cs"<
Getting Started
To install vim-sandwich using a plugin manager like vim-plug, add the following to your .vimrc
:
call plug#begin()
Plug 'machakann/vim-sandwich'
call plug#end()
Then run :PlugInstall
in Vim. To use the plugin, you can start with basic commands like sa
(add), sd
(delete), and sr
(replace) followed by a motion and the desired surrounding characters. For example, saiw(
adds parentheses around the current word.
Competitor Comparisons
surround.vim: Delete/change/add parentheses/quotes/XML-tags/much more with ease
Pros of vim-surround
- Simpler and more intuitive commands for basic surround operations
- Widely adopted and well-established in the Vim community
- Lightweight with minimal dependencies
Cons of vim-surround
- Limited customization options compared to vim-sandwich
- Lacks some advanced features like multiple character surroundings
- No built-in text object support for surroundings
Code Comparison
vim-surround:
" Change surrounding quotes from single to double
cs'"
" Add parentheses around a word
ysiw)
vim-sandwich:
" Change surrounding quotes from single to double
sdb'`
" Add parentheses around a word
saiw)
Both plugins offer similar functionality, but vim-sandwich provides more extensive customization options and advanced features. vim-surround is simpler and more widely adopted, making it a good choice for users who prefer a straightforward approach. vim-sandwich offers greater flexibility and power for those willing to invest time in learning its more complex command structure.
Add/change/delete surrounding delimiter pairs with ease. Written with :heart: in Lua.
Pros of nvim-surround
- Written in Lua, which is faster and more efficient for Neovim
- Offers more customization options and extensibility
- Provides a cleaner, more modern API for integration with other plugins
Cons of nvim-surround
- Requires Neovim 0.7+, limiting compatibility with older versions
- Less mature and potentially less stable than vim-sandwich
- Smaller community and fewer contributed configurations
Code Comparison
vim-sandwich:
nmap sa <Plug>(sandwich-add)
xmap sa <Plug>(sandwich-add)
omap sa <Plug>(sandwich-add)
nmap sd <Plug>(sandwich-delete)
xmap sd <Plug>(sandwich-delete)
nmap sr <Plug>(sandwich-replace)
xmap sr <Plug>(sandwich-replace)
nvim-surround:
require("nvim-surround").setup({
keymaps = {
insert = "<C-g>s",
insert_line = "<C-g>S",
normal = "ys",
normal_cur = "yss",
normal_line = "yS",
normal_cur_line = "ySS",
visual = "S",
visual_line = "gS",
delete = "ds",
change = "cs",
},
})
Both plugins offer similar functionality for adding, deleting, and changing surroundings in text. vim-sandwich uses Vim script for configuration, while nvim-surround uses Lua, reflecting their respective target environments. nvim-surround's setup allows for more granular control over keymaps, showcasing its enhanced customization capabilities.
repeat.vim: enable repeating supported plugin maps with "."
Pros of vim-repeat
- Lightweight and focused on repeating plugin commands
- Widely adopted and compatible with many Vim plugins
- Simple to use with minimal configuration required
Cons of vim-repeat
- Limited functionality compared to vim-sandwich
- Doesn't provide text-object manipulation features
- Requires plugins to be explicitly compatible with vim-repeat
Code Comparison
vim-repeat:
silent! call repeat#set("\<Plug>MyPlugin", v:count)
vim-sandwich:
nmap <silent> sa <Plug>(sandwich-add)
xmap <silent> sa <Plug>(sandwich-add)
omap <silent> sa <Plug>(sandwich-add)
Key Differences
- vim-repeat focuses on enhancing the
.
command for plugin actions - vim-sandwich provides comprehensive text-object manipulation
- vim-sandwich includes its own repeat functionality
- vim-repeat requires less setup but offers fewer features
Use Cases
vim-repeat is ideal for users who:
- Want to enhance existing plugins' repeatability
- Prefer a minimal, lightweight solution
vim-sandwich is better for users who:
- Need advanced text-object manipulation
- Want a more feature-rich surrounding/quoting solution
Both plugins can be used together, with vim-sandwich benefiting from vim-repeat's functionality for certain operations.
Convert
designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
vim-sandwich
sandwich.vim
is a plugin that makes it super easy to work with stuff that comes in pairs, like brackets, quotes, and even HTML or XML tags. You can quickly get rid of them, swap them out, or slap new ones around your text.
Examples
Let's dive into some quick examples. If you're inside a string with double quotes and you hit sr"'
, you'll swap those double quotes for single quotes.
"Hello world!" -> 'Hello world!'
Want to turn that into an HTML tag? Easy, just type sr'<q>
and watch it transform.
'Hello world!' -> <q>Hello world!</q>
To switch it back to double quotes, you'd do srt"
.
<q>Hello world!</q> -> "Hello world!"
To strip away those quotes, just press sd"
.
"Hello world!" -> Hello world!
Say you want to bracket the word "Hello", move your cursor there and press saiw]
.
Hello world! -> [Hello] world!
Fancy braces with some breathing room? Type sr]{
.
[Hello] world! -> { Hello } world!
Wrap the whole line in parentheses with sasb
or sas)
.
{ Hello } world! -> ({ Hello } world!)
To get back to where you started, just do sd{sd)
.
({ Hello } world!) -> Hello world!
Highlight "Hello" with an HTML emphasis tag by typing saiw<em>
.
Hello world! -> <em>Hello</em> world!
For a bigger change, like wrapping the whole line in a paragraph tag with a class, first select the line with V
and then apply S<p class="important">
.
<em>Hello</em> world! -> <p class="important"><em>Hello</em> world!</p>
This tool is a game-changer for editing HTML and XML in Vim, which is an area that doesn't have a ton of great tools right now. With vim-sandwich, adding, changing, or removing tag pairs is super simple.
If you are using vim-surround, you can use a preset keymappings similar as it. See here
Design
This plugin provides functions to add/delete/replace surroundings of a sandwiched text. These functions are implemented genuinely by utilizing operator/textobject framework. Thus their action can be repeated by .
command without any dependency. It consists of two parts, operator-sandwich and textobj-sandwich.
operator-sandwich
A sandwiched text could be resolved into two parts, {surrounding} and {surrounded text}.
-
Add surroundings: mapped to the key sequence
sa
- {surrounded text} ---> {surrounding}{surrounded text}{surrounding}
-
Delete surroundings: mapped to the key sequence
sd
- {surrounding}{surrounded text}{surrounding} ---> {surrounded text}
-
Replace surroundings: mapped to the key sequence
sr
- {surrounding}{surrounded text}{surrounding} ---> {new surrounding}{surrounded text}{new surrounding}
textobj-sandwich
- Search and select a sandwiched text automatically: mapped to the key sequence
ib
andab
- Search and select a sandwiched text with query: mapped to the key sequence
is
andas
ib
and is
selects {surrounded text}. ab
and as
selects {surrounded text} including {surrounding}s.
|<----ib,is---->|
{surrounding}{surrounded text}{surrounding}
|<-----------------ab,as----------------->|
Configuration
The point is that it would be nice to be shared the definitions of {surrounding}s pairs in all kinds of operations. User can freely add new settings to extend the functionality. If g:sandwich#recipes
was defined, this plugin works with the settings inside. As a first step, it would be better to copy the default settings in g:sandwich#default_recipes
.
let g:sandwich#recipes = deepcopy(g:sandwich#default_recipes)
Each setting, it is called recipe
, is a set of a definition of {surrounding}s pair and options. The key named buns
is used for the definition of {surrounding}.
let g:sandwich#recipes += [{'buns': [{surrounding}, {surrounding}], 'option-name1': {value1}, 'option-name2': {value2} ...}]
For example: {'buns': ['(', ')']}
foo ---> (foo)
Or there is a different way, use external textobjects to define {surrounding}s from the difference of two textobjects.
let g:sandwich#recipes += [{'external': [{textobj-i}, {textobj-a}], 'option-name1': {value1}, 'option-name2': {value} ...}]
For example: {'external': ['it', 'at']}
<title>foo</title> ---> foo
Features
Unique count handling
As for the default operators, the possible key input in normal mode is like this.
[count1]{operator}[count2]{textobject}
Default operators do not distinguish [count1]
and [count2]
but operator-sandwich does. [count1]
is given for {operators}
and [count2]
is given for {textobject}
.
Linewise and blockwise operations
Operator-sandwich works linewise with the linewise-visual selection and linewise motions.
" press Vsa(
foo ---> (
foo
)
Using command
option, user can execute vim Ex-commands after an action. For example it can be used to adjust indent automatically.
let g:sandwich#recipes += [
\ {
\ 'buns' : ['{', '}'],
\ 'motionwise' : ['line'],
\ 'kind' : ['add'],
\ 'linewise' : 1,
\ 'command' : ["'[+1,']-1normal! >>"],
\ },
\ {
\ 'buns' : ['{', '}'],
\ 'motionwise' : ['line'],
\ 'kind' : ['delete'],
\ 'linewise' : 1,
\ 'command' : ["'[,']normal! <<"],
\ }
\ ]
" press Vsa{
foo ---> {
foo
}
" press V2jsd
{ ---> foo
foo
}
Operator-sandwich also can work blockwise with the blockwise-visual selection and blockwise motions.
" press <C-v>2j2lsa(
foo (foo)
bar ---> (bar)
baz (baz)
There is an option to skip white space skip_space
, it is valid in default. Empty line is ignored.
" press <C-v>3j$sa(
fooooooo (fooooooo)
baaaar ---> (baaaar)
baaaz (baaaz)
Expression surroundings and regular expression matching
The option expr
enables to evaluate surroundings (buns
) before adding/deleting/replacing surroundings. The following recipe is an simple example to wrap texts by html style tags.
let g:sandwich#recipes += [
\ {
\ 'buns' : ['TagInput(1)', 'TagInput(0)'],
\ 'expr' : 1,
\ 'filetype': ['html'],
\ 'kind' : ['add', 'replace'],
\ 'action' : ['add'],
\ 'input' : ['t'],
\ },
\ ]
function! TagInput(is_head) abort
if a:is_head
let s:TagLast = input('Tag: ')
if s:TagLast !=# ''
let tag = printf('<%s>', s:TagLast)
else
throw 'OperatorSandwichCancel'
endif
else
let tag = printf('</%s>', matchstr(s:TagLast, '^\a[^[:blank:]>/]*'))
endif
return tag
endfunction
The option regex
is to regard surroundings (buns
) as regular expressions to match and delete/replace. The following recipe is an simple example to delete both ends of html tag.
let g:sandwich#recipes += [
\ {
\ 'buns' : ['<\a[^[:blank:]>/]*.\{-}>',
\ '</\a[^[:blank:]>/]*>'],
\ 'regex' : 1,
\ 'filetype': ['html'],
\ 'nesting' : 1,
\ 'input' : ['t'],
\ },
\ ]
However the above example is not so accurate. Instead of the example, there are excellent built-in textobjects it
and at
, these external textobjects also can be utilized through external
.
let g:sandwich#recipes += [
\ {
\ 'external': ['it', 'at'],
\ 'noremap' : 1,
\ 'filetype': ['html'],
\ 'input' : ['t'],
\ },
\ ]
Demo
Pioneers
Top Related Projects
surround.vim: Delete/change/add parentheses/quotes/XML-tags/much more with ease
Add/change/delete surrounding delimiter pairs with ease. Written with :heart: in Lua.
repeat.vim: enable repeating supported plugin maps with "."
Convert
designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot