Vim Surround command - vim

I have trouble remembering all the various keywords in vim. However, things like 'surround' from Tim Pope's surround.vim plugin are very useful.
Is it possible to set up a generic text command such that when I execute something like
:surround (.)
it will replace the current selection with
(<current_selection>)
or if I execute
:surround preamble.somethingelse
It will replace the current selection with:
preamble<current_selection>somethingelse

Alternatively you can use a custom "surrounding" which will prompt you for the text:
let g:surround_{char2nr('-')} = "\1start: \1\r\2end: \2"
Now you can visually select what you want to wrap and then press S- to tigger the - surrounding. It will then prompt you for a "start" and "ending" text.
For more help see:
:h surround-customizing
:h char2nr()
:h curly-brace-names

Almost, yes.
:command! -range -nargs=1 Surround normal gv"zc<args><Esc>F.v"zp
With this, you can create a visual selection, then use
:Surround (.)
:Surround preamble.somethingelse
Note that user-defined commands can never start with a lowercase letter, so :surround that you ask for is not possible. Also, this is a quick hack, so it's rather fragile.
However, as per comments, I would urge you to use more standard Vim methods (and well-vetted plugins like surround.vim) before cooking up custom ways to use it.
Here's another way, using surround plugin: define a custom replacement and perform it (then fix the plugin config to what it was before):
command! -range -nargs=1 Surround call ArbitrarySurround(<q-args>)
function! ArbitrarySurround(repl)
let backup = b:surround_45
let b:surround_45 = substitute(a:repl, "\\.", "\r", "")
norm gvS-
let b:surround_45 = backup
endfunction
This is more robust than the previous one, allowing ad-hoc custom replacement pairs, but does require the surround plugin. It can be used with the same syntax as the above one.

In order to leverage the surround.vim plugin, you have to base your customization on what the plugin offers. For visual mode, it's basically this mapping (that by default is mapped to S):
vnoremap <silent> <Plug>VSurround :<C-U>call <SID>opfunc(visualmode(),visualmode() ==# 'V' ? 1 : 0)<CR>
The plugin does not expose any functions (the <SID>opfunc() it invokes is script-local), so you have to invoke the <Plug>-mapping; that can easily be done with :normal (without a [!]).
Also, the plugin only takes a single character (e.g. ( to surround with parens and whitespace, ) to surround with just parens); you can reference that in your custom command via <args>.
You create a custom command with :help :command; note that the command has to start with an uppercase letter:
command! -nargs=+ Surround execute "normal gv\<Plug>VSurround" . <q-args>
This re-enters visual mode (gv), then invokes the plugin, and finally supplies the passed argument. In order to directly invoke it from visual mode, just add a -range to the definition, so that the command takes (and ignores) the '<,'> range that Vim automatically supplies then.
You can then invoke the new custom command as :Surround ), for example, and it will surround the previous visual selection with (...).

It's not exactly what you asked, but since the problem is memorizing, how about putting this in your .vimrc:
vmap ( o<ESC>i(<ESC>gvo<ESC>a)<ESC>
vmap " o<ESC>i"<ESC>gvo<ESC>a"<ESC>
Make your selection, hit ( and it gets surrounded with ( ).
Make your selection, hit " and it gets surrounded with " ".
You could do the same with {, [ or '. For such a simple use-case there's no real need for a plugin.

Related

Vim - make comment shortcut

I often write LaTeX using Vim. And I have been taught that one can comment a number of selected lines (in visual mode) using the following command:
:'<,'>s!^!%!
And similarly, one may uncomment lines in visual mode by using this command:
:'<,'>s!^%!!
Here, '%' denotes the commenting symbol for LaTeX. But I would very much like to make a shortcut to make it easier for myself to use these commands. For instance a keybinding or some sort of function so that I do not have to remember this syntax. How does one do that?
First, there are several commenter plugins, that do this very well, and those are generic (and often extensible) so that they work for any filetype, not just Latex:
NERD Commenter plugin
tComment plugin
commentary.vim plugin
are just a few popular plugins.
Custom mapping
That said, it's a good learning experience to develop a mapping on your own. Here's how:
First, mappings are just instructions that when certain key(s) are pressed, Vim translates them into other keys (on the right-hand side). Your mapping is for visual mode, so the command is :vmap. What do you normally do? You select the lines to be commented, and press :; Vim automatically inserts the '<,'> for you. You write the :s command, and conclude by pressing Enter.
Translation:
vmap <Leader>c :s!^!%!<CR>
The <Leader> is a configurable, unused key, defaulting to backslash. So, your mapping is invoked by pressing \ and then C. Put that into your ~/.vimrc to make it permanent, and you're done. Wait! There's more.
Advanced mappings
First, you should use :vnoremap; it makes the mapping immune to remapping and recursion.
Second, that mapping is global, but it applies only to the Latex filetype. So, it should apply only to Latex buffers; there's the <buffer> modifier for that.
You can define that for certain filetypes by prepending :autocmd Filetype tex ..., and put that into your ~/.vimrc. But that gets unwieldy as you add mappings and other settings for various filetypes. Better put the commands into ~/.vim/ftplugin/tex_mappings.vim. (This requires that you have :filetype plugin on.)
vnoremap <buffer> <Leader>c :s!^!%!<CR>
Technically, you should use <LocalLeader> instead of <Leader>. They default to the same key, but the distinction allows to use a different prefix key for buffer-local mappings (only if you need / like).
Let's add the alternative mapping for uncommenting, triggered via \ and Shift + C:
vnoremap <buffer> <LocalLeader>c :s!^!%!<CR>
vnoremap <buffer> <LocalLeader>C :s!^%!!<CR>
Note that you could combine both into one, using :help sub-replace-expression with a conditional expression. If anything here is over your head, don't worry. You should be using one of the mentioned plugins, anyway :-)

Vim: Issue with visual mode commands

I'm simply trying perform some commands on text that is selected in visual mode.
If I select some text & then press y (for yank) or d (for delete), it yanks or deletes the selected text
However I need to be able to do this from the command line (because I'm writing a function that I'm going to remap to one of my keys).
However when I enter the command line from visual mode '<,'> is there by default, so I just try to append y or d to the end of it leaving me with :'<,'>d
The problem with this however is that it deletes the whole line that the visual selection is in. I only want to delete the selection I made within the line.
I've tried looking at http://vim.wikia.com/wiki/Search_and_replace_in_a_visual_selection but nothing I try seems to work.
I'm sure it must be something simple but I just don't know what. Any help would be greatly appreciated.
All Vim native ex commands work on lines. I guess you can buck this trend. However you seem to be wanting to create a command simply so you can map it some key(s). You probably just want to call the function directly or creating a <Plug> or <SID> mapping instead.
Calling a function directly
xnoremap * :<c-u>call YourFunctionGoesHere<cr>
The secret sauce using :<c-u>call here. You probably want to use :normal! gv inside your function.
Using <Plug>
Here is a quick-n-dirty visual start example showing:
function! s:vstar()
let reg = ##
normal! gvy
let #/ = '\V' . substitute(escape(##, '\'))
let ## = reg
endfunction
xnoremap <Plug>(vstar) :<c-u>call <SID>vstar()<cr>/<c-r>/<cr><cr>
This show how to create the <Plug> map which shows how to call a function which is the same as calling the function directly example. The use of <SID>/s: makes sure the function is "namespace'd" to the current script.
Now you can just map * to your <Plug> mapping like in your ~/.vimrc file:
xmap * <Plug>(vstar)
This is how Vim Plugin's create their mappings. A good starting place for learning Vim plugins is :h write-plugin and looking at some very popular vim plugins. I suggest you look at Tim Pope's or Ingo Karkat's plugins as both are very prolific authors.
For more help see:
:h <Plug>
:h <SID>
:h using-<Plug>
:h write-plugin
:h c_ctrl-u
:h :norm

How to jump to a search in a mapped :normal command?

What do you need to properly jump to a matched search result?
To reproduce, make a macro with a search in it after you've run vim -u NONE to ensure there's no vimrc interfering. You'll need to make a file with at least 2 lines and put the cursor on the line without the text TEST_TEXT.
map x :norm gg/TEST_TEXT^MIthis
My intention is that when I press x, it goes to the top of the file, looks for TEST_TEXT and then puts this at the start of the line that matches the search. The ^M is a literal newline, achieved with the CtrlQ+Enter keypress. What's happening instead is either nothing happens, or the text gets entered on the same line as when I called the macro.
If I just run the :norm gg/TEST_TEXT^MIthis command without mapping it to a key, the command executes successfully.
I had an initially longer command involving a separate file and the tcomment plugin, but I've gotten it narrowed down to this.
What is the correct sequence of keys to pull this off once I've mapped it to a key?
The problem is that the ^M concludes the :normal Ex command, so your search command is aborted instead of executed. The Ithis is then executed outside of :normal.
In fact, you don't need :normal here at all. And, it's easier and more readable to use the special key notation with mappings:
:map x gg/TEST_TEXT<CR>Ithis
If you really wanted to use :normal, you'd have to wrap this in :execute, like this:
:map x :exe "norm gg/TEST_TEXT\<lt>CR>Ithis"<CR>
Bonus tips
You should use :noremap; it makes the mapping immune to remapping and recursion.
Better restrict the mapping to normal mode, as in its current form, it won't behave as expected in visual and operator-pending mode: :nnoremap
This clobbers the last search pattern and its highlighting. Use of lower-level functions like search() is recommended instead.
There are many ways of doing this however this is my preferred method:
nnoremap x :0/TEST_TEXT/norm! Itest<esc>
Explanation:
:{range}norm! {cmd} - execute normal commands, {cmd}, on a range of lines,{range}.
! on :normal means the commands will not be remapped.
The range 0/TEST_TEXT start before the first line and then finds the first matching line.
I have a few issues with your current mapping:
You are not specifying noremap. You usually want to use noremap
It would be best to specifiy a mode like normal mode, e.g. nnoremap
It is usually best to use <cr> notation with mappings
You are using :normal when your command is already in normal mode but not using any of the ex command features, e.g. a range.
For more help see:
:h :map
:h :norm
:h range
try this mapping:
nnoremap x gg/TEST_TEXT<cr>Ithis<esc>
note that, if you map x on this operation, you lost the original x feature.

How to create an alias for ctags in vimscript

I have a function that is invoked based on a simple key mapping
function! JumpToDefinition()
let filetype=&ft
if filetype == 'coffee'
exe '<C-]>'
endif
endfunction
This works when I manually do <C-]> but when I try to "exe" it above I get a "trailing whitespace" error.
How can I invoke this in a standalone function like I have above?
Note that :execute runs the resulting expression as an Ex command,
which isn't probably what you want since there is no <C-]> Ex command. You
should be using :normal.
However, to be able to use these "special keys", instead of the characters
they represent, you have to pay attention to three things:
The correct way to represent them is with a backslash \<xxx>. Check the
help for expr-string.
Use double quotes, not single quotes
:normal accepts commands, not an expression like :execute
So, from items 1 and 2 above we know that "\<C-]>" should be used, but you
can't put this in front of :normal. Well, you can, but then it will be
executed as "quote, backslash, C, ...". The solution is to go back to using
:execute, this time to build the string with the visible "\<xxx>" in front
of :normal, which will be expanded to the actual character and executed.
:exe "norm \<C-]>"

How can I call an editor command from a vimscript?

I want to remove an unwanted scrollbar from taglist. I created a function and a command like this:
function s:TlistWaToggle()
normal :TlistToggle<cr> " <- this does not work
set guioptions-=r
endfunction
command! -nargs=0 -bar TlistWaToggle call s:TlistWaToggle()
I want to wrap the call to :TlistToggle together with the command to remove the right scrollbar (I have that setting of course, but it always reappears, so this is a workaround). Currently my :TlistWaToggle doesn't do anything. How can I make it work?
Vim script uses ex commands, and apparently :TlistToggle is an ex command…
function! s:TlistWaToggle()
TlistToggle
set guioptions-=r
endfunction
In addition to #sidyll's answer: :normal is not a :*map, it accepts only raw character strings. Correct command will be execute "normal! :TlistToggle\<CR>" (or execute "normal! :TlistToggle\n"). Note that you should not use non-banged version in your scripts.
I don't think you will ever use :normal! to execute an ex command, but my answer would be useful when you want to pass any other special character. It also applies to feedkeys() call.
By the way, comments and other commands will be considered part of string passed to :normal command.

Resources