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
Related
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.
I'm trying to create a mapping on Vim, that behaves like the following:
First of all I select a block of code or text using on Visual mode.
The I use this mapping to substitute the first column on each line for '#' effectively commenting each line.
Up until now I have the following:
vnoremap <Leader>c :normal! :s/^/#/<cr>
But for some reason it is not working. Nothing happens when I hit <Leader>c on a block of text. On the other hand, if I have:
vnoremap <Leader>c :normal! s/^/#/<cr>
it will for example subsitute:
The grey fox.
For
/^/#/he grey fox.
Any idea on how can I solve this issue?
:normal is an Ex command that allows to execute normal mode commands (from a custom command or function, or command-line mode in general). Your keys start off with :, so immediately switch from normal (or here: visual) mode to command-line mode. That doesn't make sense. Just define the mapping like this:
vnoremap <Leader>c :s/^/#/<cr>
The : will automatically insert '<,'> for you, and that's what you want here (to operate on all selected lines). You can also define a related normal-mode mapping that works on the current (or [count]) lines:
nnoremap <Leader>c :s/^/#/<cr>
If the highlighting disturbs you, append the :nohlsearch command:
nnoremap <Leader>c :s/^/#/<bar>nohlsearch<cr>
You can simply do:
vnoremap <leader>c :s/^/#/<cr>
The substitution command will get the range, '<,'>, automatically when in visual mode.
Note: you probably want to use xnoremap instead of vnoremap.
There is a better way
Commenting is a common problem, deceptively tricky, and already solved by many plugins. I prefer to stand on the shoulder's of giants and use a plugin. I personally use commentary.vim.
I've recently found a vim plugin using something called <Plug>. For example there is a command <Plug>abc_def which I would like to execute.
I've tried everything like :<Plug>abc_def and similar. Nothing worked. And :help <Plug> gave no information.
However, I've been able to execute it by creating a mapping :map x <Plug>(unite_redraw). Then I can execute it by pressing x.
Now, is there any way to execute :<Plug>abc_def without creating a dummy mapping just to run it? The actual plugin I use is Unite.
<Plug> mappings are meant to be mapped and called via the map. A <Plug> map is a device to expose a clean interface of plugin actions to the user.
Example: Instead of mapping some key to some plugin function in the plugin's hard code, such as "map zz to the action 'center cursor aesthetically'":
nnoremap <expr> zz 'zz'.float2nr(winheight(0)*0.1).'<C-E>'
it is better to expose only a named <Plug> mapping from the plugin:
nnoremap <expr> <Plug>NiceCenterCursor 'zz'.float2nr(winheight(0)*0.1).'<C-E>'
that the user can then remap in their config, without having to copy and paste the "action":
nmap zz <Plug>NiceCenterCursor
This is then easy to override, reuse, plug into by the user.
<Plug> mappings are active only in the modes they have been defined for. To execute a <Plug> mapping that is defined for normal mode, you can do as with any normal command: use :normal (without the exclamation mark).
:execute "normal \<Plug>NiceCenterCursor"
Since <Plug> actually represents a special magic key, we need to use :normal together with :execute and escape the <Plug>.
The <Plug> mechanism is described in depth at :h 41.11. See also this article about this topic by a Vim master.
I'd like to map F2 in VIM so that it first saves the file with :w and then calls a script /home/user/proj/script.sh that will upload the changed files to the server.
I already tried
:map F2 :w<cr>|:! /home/user/proj/script.sh
but that doesn't work.
Please tell my why this isn't working and help me to get this working.
Try :noremap <F2> :<c-u>update <bar> !/home/user/proj/script.sh<cr>.
noremap vs. map
This creates a non-recursive mapping. See Wikia - Mappings keys in Vim
I just assumed this, because it's what people want most of the time. :-)
update vs. write
Only save the file if there were actual changes. See: :help :update.
<bar> vs. |
:help map_bar
<c-u>?
You used a generic mapping, instead of one for a certain mode, like nmap for normal mode mappings. Thus the mapping could also be triggered in visual mode. If you select something in visual mode and hit :, you'll see the commandline is prefixed with a range. But you don't want that in your case and <c-u> clears the commandline.
<cr> at the end?
Your mapping drops into the commandline mode and inserts 2 things separated by <bar>. Afterwards it has to execute what it has written, thus you need to append a <cr>.
I am trying to do a comment remap in Vim with an inline if to check if it's already commented or not. This is what I have already and of course it's not working ha ha:
imap <c-c> <Esc>^:if getline(".")[col(".")-1] == '/' i<Delete><Delete> else i// endif
What I want to do is check the first character if it's a / or not. If it's a / then delete the first two characters on that line, if it's not a / then add two // in front of the line.
What I had originally was this:
imap <c-c> <Esc>^i//
And that worked perfectly, but what I want is to be able to comment/uncomment at a whim.
I completely agree with #Peter Rincker's answer warning against doing this in insert mode, and pointing you to fully-featured plugins.
However, I couldn't resist writing this function to do precisely what you ask for. I find it easier to deal with this kind of mapping with functions. As an added bonus, it returns you to insert mode in the same position on the line as you started (which has been shifted by inserting or deleting the characters).
function! ToggleComment()
let pos=getpos(".")
let win=winsaveview()
if getline(".") =~ '\s*\/\/'
normal! ^2x
let pos[2]-=1
else
normal! ^i//
let pos[2]+=3
endif
call winrestview(win)
call setpos(".",pos)
startinsert
endfunction
inoremap <c-c> <Esc>:call ToggleComment()<CR>
Notice the modifications to pos to ensure the cursor is returned to the correct column. The command startinsert is useful in this type of function to return to insert mode. It is always safer to use noremap for mappings, unless there is a very good reason not to.
This seems to work well, but it is not very Vim-like, and you might find other plugins more flexible in the long run.
There are many commenting plugins for vim:
commentary.vim
tComment
EnhCommentify
NERD Commenter
and many more at www.vim.org
I would highly suggest you take a look at some these plugins first before you decide to roll your own. It will save you great effort.
As a side note you typically would want to comment/uncomment in normal mode not insert mode. This is not only the vim way, but will also provide a nicer undo history.
If you are dead set on creating your own mappings I suggest you create a function to do all the hard work and have your mapping call that function via :call. If you think you can get by with simple logic that doesn't need a function then you can use an expression mapping (see :h map-<expr>). You may want organize into a plugin as it could be large. If that is the case look at :h write-plugin to give you a feel
for writing plugins the proper way.
Example of a simple expression mapping for toggling comments:
nnoremap <expr> <leader>c getline(".") =~ '\m^\s*\/\/' ? '^"_2x' : 'I//<esc>`['
there's also this vimtip! http://vim.wikia.com/wiki/Comment/UnComment_visually_selected_text
i use the bottom one with the
...
noremap <silent> ,c :<C-B>sil <C-E>s/^/<C-R>=escape(b:comment_leader,'\/')<CR>/<CR>:noh<CR>
noremap <silent> ,u :<C-B>sil <C-E>s/^\V<C-R>=escape(b:comment_leader,'\/')<CR>//e<CR>:noh<CR>
,c comments out a region
,u uncomments a region