I want to map some combinations. I have been reading :h map for a long time, but I still don't understand how to map "d" and "y" so that they do the following things ({ } is optional):
d{count}{movment} => "_d{count}{movment}
{"reg}<Leader>d{count}{movment} => {"reg}d{count}{movment}
I want d to really delete text, so that the familiar d3d, d3w, diw, etc will really delete text too. <Leader>d to cut into register "1 (if not specified) or into specific register.
y{count}{movment} => y{count}{movment} call my_func()
"reg y{count}{movment} => "reg y{count}{movment}
Same thing with y, but here y{count}{movment} must copy and call my function. And "reg y{count}{movment} just should work as usual.
I wish it would work visual and normal mode.
This is evil and should not be done. It only makes sense if you seek for personal "challenge", yet in this case you'd better figure it yourself. But if you still need a hint, I'd probably started with :h 'opfunc'
That is easily achievable if your Vim is modern enough (8.1+). Simply catch :h TextYankPost and do your stuff from there.
Related
The m normal command accepts a letter just after it to set a "letter" mark.
I would like to create a similar command that works across tabs... But my problem is with the binding : Is there a simple way to bind for example M<letter> to a function or a command or should I manually repeat all the possibilities ?
As romainl has already said, no.
Covering this for good measure (and in case someone else comes along later), you can only practically map upper-case letters. As is outlined in the documentation, lower-case marks are only valid within a single file. Upper-case ones, that the Vim docs calls "file marks", are valid from anywhere. Unless you have some dark magic function to resolve ambiguous file marks, you probably only need a single for loop mapping the upper-case letters, if you're going with the brute-force option.
That said, there are a couple alternatives here as well.
As far as I know, the only "dynamic" bit of a command is a count (or a range, but unless you want to map characters to a number (and handle ranges and other fun stuff:tm:), I don't recommend this approach:
" The <C-U> is required for all uses. If you want a function,
" you'd need :<C-U>call YourFunction()<cr>
nnoremap M :<C-U>echom v:count<cr>
See also :h v:count, which states:
Note: the <C-U> is required to remove the line range that you get when typing ':' after a count.
You can then run 26M, decode v:count as Z, and then do whatever fancy lookup from there.
The second alternative, and the one proposed by romainl and by far the most used one in cases like this (source: experience and lots of code browsing), is using a for loop to brute-force map letters:
for i in range(char2nr('A'), char2nr('Z'))
exec 'nnoremap M' . nr2char(i) ':echo "This is where the appropriate function call goes"<cr>'
endfor
Which produces all 26 mappings.
And the final approach is abusing getchar(). This means a single mapping, but at the expense of doing additional processing:
func! Func()
let c = getchar()
echo c
" Character processing (the `echo` isn't required) here
endfunc
nnoremap M :call Func()<cr>
You can decide which works for you, but I strongly suggest preferring option 2, as this gives you map timeouts and clear definitions, and more obvious conflict detection.
My autocomplete for vim inserts something like this for C++ code:
if( ){
}
Inside the () is where my cursor is going to be. Suppose I want to type
if(X[i] == 0){
}
I would type X, then [ and the closing brace will automatically be inserted, ].
I then type i, now my cursor is between i and closing ]. How do I get out so that I can keep typing the rest, the == 0 part?
If there is no autocomplete for the if statement, then it would be easy because I do not have to worry about the ( ). I can just type, if, (, X[i]. To get out of the bracket, I would just use append, A and then go on to type the rest: == 0 ).
Normally, I would use the arrow key or hl to move out of the brackets. Is there another way to do this?
There are several parts to your problem, and plugins that solve them -- of course you could reimplement them...
Regarding the auto-pairing, most (all?) bracketing plugins provide a jump over the closing bracket character by hitting the related key. Here for instance you could simply type ] instead of <right> or <esc>la as you would have done without the auto-pairing feature.
Note: contrary to most solutions you could copy paste from a 1990's vi trick, the plugins will permit to redo (:h .) an insertion where brackets appear.
I'm quite sure the exact technical solution has already been given here and/or on vi.SE.
There is another issue you haven't mentioned: jumping to in-between the curly brackets corresponding to the true branch of the if. To achieve this, we rely on placeholders. They could be inserted by snippet plugins, or through very advanced abbreviations.
The trendy snippet plugins have discrete placeholders. The plugins I'm maintaining have more intrusive placeholders (lh-cpp, mu-template. Placeholders that my bracketing plugin also inserts by default. What does this means? It means that typing if or [ will insert the same placeholders that also serve as jump points. As a consequence, I can use the same jump keybinding (CTRL+J by default in terminal vim) to jump after the closing ] or in between the {} -- hitting ] works as well when the cursor is just before a ], etc.
From years of "finger memory" my hands know that F2 is save and F3 is quit (leftovers from years of IBM editors). Consequently my first vim key mappings were to get F2 and F3 doing what they're supposed to do. In particular for F3:
:map <F3> :q<CR>
:map! <ESC>:q<CR>
If I quit a file that has edits I still get the E37: No write since last change (add ! to override). What I'd prefer is more a function that would emit E37: No write since last change. Press F3 again to really quit.)
How would I code this?
Updated Solution Suggestion
It was pointed out to me that I could have used confirm to accomplist this task. I "knew" I'd seen some built-in solution before but i couldn't remember it. Note, however, confirm's sense is sense is inverted from the macro's, i.e. it offers "Yes, keep changes" while the macro offers "yes, discard changes." I'd have settled for confirm` if only it had been suggested earlier.
What I Actually Did
First, thanks #Jamie Schembri for the starting example. Always good to have an example when learning a new scripting language.
Here is the macro as I finally used it. Yes, it does not quite parallel the requirements. I replaced the second F3 with a Yes|No|Write choice which just felt better. I also had to add a nr2char() so the test for letters would work. I also had to delete one of the CR's from the :map.
UPDATE see this question for a solution using this macro with added functionality to handle tagstack files. If you quitfrom a file in the tagstack, vim closes all your files.
function! QuitF3()
try
quit
catch /E37:/
" Unwritten changes.
echo "E37: Discard changes? Y|y = Yes, N|n = No, W|w = Write"
let ans = nr2char( getchar() )
if ans == "y" || ans == "Y"
quit!
elseif ans == "w" || ans == "W"
write
else
call feedkeys('\<ESC>')
endif
endtry
endfunction
Referring to pandubear's answer, I wrote a quirky little function which seems to do what you want.
function! QuitF3()
try
quit
catch /E37:/
" Unwritten changes.
echo "E37: No write since last change. Press F3 again to really quit."
if getchar() == "\<F3>"
quit!
else
" Close prompt.
call feedkeys('\<ESC>')
endif
endtry
endfunction
map <F3> :call QuitF3()<CR><CR>
I think you could map F3 to a function that does this sort of thing: (pseudocode)
try:
quit
catch error 37:
intercept next keystroke
if it's F3:
force quit
else:
send key back to vim
I don't know all the details, but I'm pretty sure you can do all of those things with :try, :catch, getchar() (iffy on this one, not sure if it'll work for special keys like F3) and feedkeys(). See the :help for each of those things.
Seems a little bit much, though. Why not make a separate mapping for :q!?
In Vim there are basically two types of commands that can make it go into insert mode:
Commands that just add something, such as: i, I, a, A (apart from using backspace).
Or, that also remove a piece of text, such as: c[motion], C, s, v[motions]s.
I would like to hook the InsertLeave event, but in my code I need to know which type of change it was (an insert like i, or a change like cw). Is there any way to find that out?
Would i<BS><BS><BS>bar count as insertion or change? If the latter, you could :undo the change on InsertLeave, store the lines affected by it (i.e. '[,']), :redo, then compare both sets. If there's "just more text", it's an insertion, else a change.
There is one difference that you may be able to exploit: The change commands all modify a register (unless the black-hole register was explicitly specified by prepending "_), whereas insertions do not (well, except for ".).
If you take a "snapshot" of the default register before (e.g. with a CursorMoved,CursorHold combo) and compare the contents on InsertLeave, you can find out.
I know that when you define a function in vim, you can use the range keyword so that users can say:
:-5,+5call MyFunction()
And then your function gets a:firstline and a:lastline.
What I want is something similar, but more like the traditional vi way of combining a command with a movement, so that 'd ' deletes a space, 'dw' deletes a word, 'd2w' deletes two words, 'd2j' deletes three lines, etc. Assuming my function gets mapped to some input-mode character sequence, is there any way to make it accept similar variable-length inputs, and then modify that text?
Just to be a little more clear, suppose I want to define a function to wrap <b> tags around existing text. We'll say that function is mapped to ;b. I want users to be able to say ';bw' to bold one word, or ';bf.' to bold everything to the end of the sentence, or whatever, with all the flexibility that vim provides to built-in commands.
If I understand what you're asking, then all you do is include the char argument in your mapping. For example:
map d :call MyFunction('d')<cr>
map dw :call MyFunction('dw')<cr>
map d2w :call MyFunction('d2w')<cr>
" Of course these would be terrible mappings because they
" are already mapped to important Vim functions
The way mappings work is that if you "overspecify" a char like 'd' above so that it is usable either by itself or as a prefix for longer command, Vim will wait briefly (for timeoutlen)after you press 'd' to see if you're going to press another character. (This depends on thetimeout option being set to true, which is the default.) If you don't press another character, Vim will execute the 'd' mapping. If you do it will call the more complex mapping. See :h map-commands generally and :h map-typing for more.
Note: After your clarification I think what you want is to create a custom 'operator' function that you can use to operate on buffer areas defined by Vim motions. See :h map-operator for info on how to do this.