One thing I really like in Vim is it's hability to have multiple clipboards available. However I hate to write "ay to yank and "ap to paste, I'd rather have something closer to the classical Ctrl-c, Ctrl-v, like ←a (←: AltGr + y) and þa (þ: AltGr + p).
I could make a remap like nnoremap ←a "ay in this case, but then I would only have the buffer "a" available to use this way. So the question is: could I make a remap such as nnoremap ←{key} "{key}y in vim, that would replace the {key} with whatever I typed, so that I could use any character as a register with only one remap? (←q becomes "qy, ←w becomes "wy, etc...)
Btw: yes, AltGr keys like "←" and "þ" works just like any other letter for commands.
The left-hand side of a mapping can't be dynamic.
The easiest way to deal with that limitation is simply to loop through a list:
for reg in 'abcdefghijklmnopqrstuvwxyz'
execute 'nnoremap ←' .. reg .. ' "' .. reg .. 'y'
execute 'nnoremap þ' .. reg .. ' "' .. reg .. 'p'
endfor
See :help :for, :help :execute, :help expr-...
Note that y is an operator and an operator is supposed to "operate" on a motion. This means that all those "{char}y normal mode mappings are useless. For this to actually be useful, you would need to:
make visual mode mappings for y,
make a custom operator for use in normal mode, see :help :map-operator.
Related
I have my .vimrc in a different path, that I source from my main ~/.vimrc (so I can share same settings across Windows, bash on Windows, etc).
I'm trying to write something in the .vimrc in question, that would make a hotkey for editing said .vimrc, without hard coding the path.
What I currently have is this:
let g:vimrc_path = expand('<sfile>')
:map <Leader>v exec(":e " + g:vimrc_path + "<CR>")
But this doesn't seem to do anything. I've verified that g:vimrc_path is the right value, and that the <Leader>v ends up being called by subbing in echo messages, but I'm not wrapping my head around why the variable I'm trying to define doesn't get expanded correctly.
String concatenation is done with ., not with +, which performs coercion into numbers and addition. But :execute takes multiple arguments (which it space-separates), so you don't actually need this here.
You should use :noremap; it makes the mapping immune to remapping and recursion.
Also, I doubt you need visual and operator-pending modes (:help map-modes), so define this just for normal mode.
:exec[ute is an Ex command, so for a normal-mode mapping, you need to first enter command-line mode. So :exec 'edit' instead of exec ':edit'.
Also, this is not a function (though Vim 8 now also has execute()), so the parentheses are superfluous.
The <silent> avoids the printing of the whole command (you'll notice the loading of the vimrc file, anyway); it's optional.
The fnameescape() ensures that pathological path names are also handled; probably not necessary here.
let g:vimrc_path = expand('<sfile>')
nnoremap <silent> <Leader>v :execute 'edit' fnameescape(g:vimrc_path)<CR>
Alternative
As the script path is static, you can move the variable interpolation from runtime (mapping execution) to mapping definition, and get rid of the variable:
execute 'nnoremap <Leader>v :edit' fnameescape(expand('<sfile>')) . '<CR>'
Strings in vimscript are concatenated with ., not with +. For example:
:echo "Hello"." world!"
will echo
Hello world!
If you were to type
:echo "Hello" + " world!"
vim would echo
0
This is because the + operator is only for numbers, so vim attempts to cast these strings to numbers. If you were to run
:echo "3" + "1"
vim would echo "4".
So basically, you just need to change
:map <Leader>v exec(":e " + g:vimrc_path + "<CR>")
to
:map <Leader>v exec(":e ".g:vimrc_path."<CR>")
Another problem you might have not seen is that "<CR>" evaluates to the literal text "<CR>", so it only messes up your function. If you want a literal carriage return, you would need a backslash. However, you definitely do not want to do this! Seriously, try it out and see.
You can see the issue. It looks for a file that has a literal carriage return at the end of the filename! There is a very simple fix though. Remove the "\<cr>" completely. Since :exec runs ex commands by default, the carriage return (and the colon too for that matter) are unnecessary.
Also, as a nitpick,
The parenthesis are not needed for the "exec" function, and
Use nnoremap instead to avoid recursive mappings.
Taking all of this into consideration, I would simplify it to
:nnoremap <Leader>v :exec "e ".g:vimrc_path<cr>
Maybe this goes against the traditional zen of VI, but I get very overwhelmed when it comes to working with rebinding keys in Vi, in particular what can/cannot be bound. I've had a particular headache since moving to nvim with selecting/copying/pasting using visual mode, as well as no longer having access to Home/End to jump to beginning/end of a line while in insert mode.
I was thinking that a way around this, much like I do with my tmux config, is issue a command to unbind all keys in my vimrc, then rebind them all. That way, there's no confusion to what a binding is, and I can easy change and reference a change anytime. Am I going about this all wrong?
You can't really do this. Vim treats "built-in mappings" different from "user-defined mappings". You can't really "unmap" a built-in mapping.
Technically, it stores built-in mappings in a different C struct than user-defined mappings defined with :map. When you do :unmap, it just removes it from the user-defined struct (incidentally, I gave a brief − but incomplete − overview of how this works last week in How to find out what a key is mapped to? at vi.SE).
The only thing you can do is something like:
" Remap all ascii characters; everything below 33 is a control character
for i in range(33, 127)
" | needs some extra love
if i == 124 | continue | endif
execute 'nnoremap ' . nr2char(i) . ' <Nop>'
execute 'nnoremap <C-' . nr2char(i) . '> <Nop>'
endfor
" The above won't remap stuff like `<F1>`, `<Up>`
nnoremap <Bar> <Nop>
nnoremap <F1> <Nop>
nnoremap <Up> <Nop>
" ... etc ... You can use a loop for this as well...
" Now make our own mappings
nnoremap : :
" ...etc...
And the same for vnoremap, etc. but this won't remap <C-w><C-w>, gJ, etc. so you'll need to add even more exceptions for that (the "second key" for these mappings isn't even in a struct, but is a switch/case!)
However. don't do this. Because now rely on your vimrc that you and only you can understand. Just learn the default mappings. This will mean you can use any Vim installation out-of-the-box, and your mappings won't be somehow "better" than the default mappings.
You can put this line near the top of your vimrc to reset all options to their default value:
set all&
But you can't realistically hope to "unbind" the "default bindings" with a single command because:
they are not "bindings", they are "commands",
there's no such command anyway.
You could remap every default command to <nop> (:help <nop>) but that sounds like a lot of work for very little benefit.
I want a movement to get from point 1 to points 2 and 3:
FunctionCall(a, b, c, AnotherFunc(a, b))
^ <--- ^ ------------> ^
3 1 2
Ideally it would work with editing operations like d, y etc
Now I count closing parentheses visually and use <n>f), but it's very annoying.
Vim does have facilities to know about blocks, i.e. i(, a(, but I want 'a half' of such a command.
How can I achieve this?
What I usually do is using a suitable commands from various-motions.
For example, for your particular case ]) followed by % will help.
They can be combined with operations like d, y and c.
More details using :help various-motions
You are right that the text objects have knowledge about both sides of the block. If you briefly go into visual mode, your cursor will end up on the end of the range (va(<Esc>) or the beginning (va(o<Esc>) when using the v_o command to switch to the "other" side of the selection.
Unfortunately, because this involves multiple steps, it only works for navigation, but not as an atomic {motion} to be used with d, y, etc. You could write a custom motion for those, though. For example:
onoremap ,) :normal! va(<C-v><Esc><CR>
onoremap ,( :normal! va(o<C-v><Esc><CR>
These then work like motions, e.g. gU,) uppercases the text from the cursor to the closing parenthesis.
The normal mode command % will take you to the next opening paren (and while there, toggling between the opening and closing parentheses). So sadly it would jump to the innermost opening paren instead of place 2 or 3.
Couple that with a F( and you've got something remotely neat that doesn't involve counting. So in place 1, F(% would take you to place 3 and a subsequent % would jump you to place 2.
You can do the following:
nnoremap <silent> ,) :normal! va(<c-v><esc><cr>
nnoremap <silent> ,( :normal! va(o<c-v><esc><cr>
onoremap ,) :<c-u>normal! mava(o`a<cr>
onoremap ,( :<c-u>normal! mava(`a<cr>
Note that for the motion version (onoremap), it consumes the a mark, which can be a problem; so it could be improved.
I'm trying to have remap i/c/o/a and friends to always record a macro. But the keypress that actually brings me into insert mode is not recorded (explained below).
For example, if we have:
nnoremap i qzi
inoremap <esc> <esc>q
Ideally, this means that when I enter insert mode (i), I also start recording to register z. And when I leave (<esc>), we stop recording. So register z should have the entire sequence needed to repeat what I did in insert mode.
But then if I try to type in insert mode (e.g., ihelloworld<esc>) and then check :registers a, I get
---Registers---
"z helloworld^[
Notice how the i command is not included -- only what i typed in insert mode is.
Why is this happening? What can I do to get around it?
I also noticed that if I just typed qzihelloworld<esc> normally, the z register DOES contain ihelloworld<esc> (with the i).
For those interested, I'm doing trying to do this because certain operations break 'repeat last command (the period key .)'. For example, if you use <c-o> or move around while insert-mode, . will only repeat the last segment of your insert mode. In particular, I'm trying to find a workaround for an autoclosing plugin
As #Carpetsmoker explains why it doesn't get recorded:
From :help q
The 'q' command is disabled while executing a register, and it doesn't work inside a mapping and :normal.
So that explains why it doesn't work ...
A solution:
As the command (e.g., i) in the mapping is not recorded, a solution is to prepend it to the recorded buffer when leaving insert mode.
To distinguish between i, a, o, etc, you can store the command in a variable, and then prepend that to the recorded macro:
nnoremap i :let b:mode="i"<cr>qzi
nnoremap a :let b:mode="a"<cr>qza
nnoremap o :let b:mode="o"<cr>qzo
inoremap <Esc> <Esc>q :let #z=":normal! ".b:mode.#z<CR>
Simply prepending i (etc.) to the macro doesn't work (for me) since it triggers the mapping for i. Calling it with normal! fixes that.
Another option is to modify the macro to enter insert using startinsert (combined with suitable movements to emulate i, a, o, etc.) by prepending the macro like this
:let #z=":startinsert^M".#z
caveat: Your rationale for wanting this:
For example, if you use <c-o> or move around while insert-mode, . will only repeat the last segment of your insert mode.
(my emphasis) needs the warning that "moving around" (with arrow keys) does not work (at least in my quick tests) without additional hacking, as the arrow keys send an <esc> ... sequence, triggering the end of the recording mapping for <esc>. c-o will work though, so you can move around (slowly) with e.g. <c-o> h (for moving left).
Another problem is that replaying it as a macro does not work (with this solution at least, perhaps due to the use of normal!) if you have newlines, etc., in the recorded macro. A solution to that is to execute the buffer instead of replaying it as a macro. That is, :exe #z instead of #z for instance with the mapping
nnoremap <space>z :execute #z<cr>
I have the following keyboard shortcut in VIM
nnoremap C vi"
This enable me to select text between "" by clicking C.
Now I want this same shortcut, to do the same but for ''.
nnoremap C vi'
Putting these two rules doesn't work, as the last replaces the one before it.
Is there a way to make both of them work?
Yes, but you need to build the intelligence into the mapping. This can be done via a :help :map-expr:
nnoremap <expr> C 'vi' . (getline('.') =~ '"' ? '"' : "'")
This simplistic example will check whether the current line contains a double quote, and then select those, else single quotes. For a useful mapping, you probably need to ensure surrounding quotes on both sides (using search()), and if both types match select the "closer" one. With a :function, you can make that as complex as you like...