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!?
Related
we all know what ci" ci' ci( ci[ ... does. Very handy in everyday's editing. I found something strange, and checked the help, didn't find out why.
say, I have a file:
foo "target"
foo 'target'
foo (target)
foo {target}
foo [target]
foo <target>
if my cursor at the beginning of each line, (on the 'f'), then I type ci", ci', ci(...
the cix works only with quotes (single or double), doesn't work for brackets. why do they behave differently?
(dix, vix the same)
tested with --noplugin, vim 7.3
thank you.
Update
thanks #romainl for the answer. I still have doubt regarding the "pair processing in vim"
check this example:
foo "targ\"eti\" some\"thing else "
if I have a line like above, I type ci", no matter cursor is at beginning or between quotes, it works perfectly, it seems that vim does have the idea of "pair"?
and this maybe what you meant about the pairing ?
foo "target x some"thing else "
foo (target x some(thing else )
I have above two lines, if (cursor at x) I type ci" and ci(, nothing happened to 2nd line, but first line changed into:
foo "I"thing else " (I is cursor)
ci( is consistent with ci[, ci{ and cit and all the other <action>i<something>. Only ci' and ci" work like they do. The outliers are the quotes, here, not the brackets.
Vim doesn't consider that quotes come in pairs while brackets do. It has an internal logic for matching pairs that works with actual pairs but not with quotes hence the difference in behavior.
You are not the first to complain about that discrepancy: this is one solution, maybe you can find others.
edit
I don't have a deep knowledge of Vim's internals, unfortunately, so I can only make assumptions, here.
If you ask Vim to do ci" it does its best to find a pair of double quotes but double quotes don't go by pairs: there's no way to tell if a " is the closing one or the opening one contrary to brackets. Because of that, Vim must make some choices. IMO, the choice that would make the most sense considering how the other members of the family work, would be to assume that the cursor is between the quotes and select from the first one to the right to the first one to the left. I can only assume that this method proved wrong in some way or didn't work for some reason and that the other method (the current one) prevailed.
Another explanation could be that the i<something> mechanism is somehow tied to a specific subsystem (maybe the same as showmatch?) that is unable to deal correctly with quotes.
Anyway, just like you, I find this discrepancy weird and I've somehow internalized it and aligned my usage of <action>i" to how the others work. To the point of actually doing 2t"ci" or some variant instead of ci"!! Inefficient, I know.
Did you read :h a'? I completely forgot where I got my "limited understanding" of the issue but it was there! It says:
"Only works within one line. When the cursor starts on a quote, Vim will figure out which quote pairs form a string by searching from the start of the line."
What I get from that is this: for some reasons unknown to us, Vim uses another mechanism for matching quotes than for the other pairs and that is why ci" is different from ciband friends. The underlying cause is not clear at all but I'm fairly certain that the big picture looks a lot like what I imagine.
To me, it looks a lot like a bug or a limitation disguised as a feature.
If you are still curious, I'd suggest you ask any further question on vim-dev.
make use of %
" nnoremap cb cib
nnoremap cb %cib
" nnoremap vb vib
nnoremap vb %vib
nnoremap yb %yib
nnoremap db %dab
to enhance %:
https://github.com/andymass/vim-matchup#tocbar-dopjfd
b can match ( [ {,
Want to use ' to match both ' and "?
nnoremap c' :call DoubleAsSingleC()<CR>
func! DoubleAsSingleC()
" When [!] is added, error messages will also be skipped,
" and commands and mappings will not be aborted
" when an error is detected. |v:errmsg| is still set.
let v:errmsg = ""
silent! :s#\"\([^"]*\)\"#'\1'#g
if (v:errmsg == "")
echo "双变单"
endif
exec "normal ci'"
endfunc
Similar for:
d'
y'
v'
When I am yanking into registers, I often mistype the "x syntax, so I would like to have a confirmation of which register it used to yank the text.
For instance, my ideal output if I were to type "x3yy would be "3 lines yanked into x" instead of the current "3 lines yanked". Is it possible to modify this report somehow? Ideally it would also work for deleting, etc.
This has been addressed in recent Vim builds, starting with version 8.0.0724: the message for yanking doesn't indicate the register.
Once you've upgraded (either by waiting until a package for your operating system is available, or compiling Vim yourself), the message on for example "a4yy will be:
4 lines yanked into "a
This feature was added to the after patch 8.0.0724 (kudos for Ingo Karkat for reporting it).
Neovim, as of now, hasn't merged this patch. However, it implements TextYankPost, which allows you to hack a similar behavior, as it provides the type of operation, the register used and the contents that was copied to the register.
Taking that into consideration, the following code snippet would do what you are asking for:
function! s:better_operator_message()
let number = len(v:event['regcontents'])
if v:event['operator'] == 'c' || v:event['operator'] == 'd'
let message = number . ' fewer lines'
elseif v:event['operator'] == 'y'
let message = number . ' lines yanked'
else
return
endif
if v:event['regname'] != ''
let message = message . ' into register ' . v:event['regname']
endif
echom message
endfunction
set report=10000000000
augroup better_operator_message
autocmd!
autocmd TextYankPost * call <sid>better_operator_message()
augroup end
I've made this snippet available as a plugin, in case you're interested.
we all know what ci" ci' ci( ci[ ... does. Very handy in everyday's editing. I found something strange, and checked the help, didn't find out why.
say, I have a file:
foo "target"
foo 'target'
foo (target)
foo {target}
foo [target]
foo <target>
if my cursor at the beginning of each line, (on the 'f'), then I type ci", ci', ci(...
the cix works only with quotes (single or double), doesn't work for brackets. why do they behave differently?
(dix, vix the same)
tested with --noplugin, vim 7.3
thank you.
Update
thanks #romainl for the answer. I still have doubt regarding the "pair processing in vim"
check this example:
foo "targ\"eti\" some\"thing else "
if I have a line like above, I type ci", no matter cursor is at beginning or between quotes, it works perfectly, it seems that vim does have the idea of "pair"?
and this maybe what you meant about the pairing ?
foo "target x some"thing else "
foo (target x some(thing else )
I have above two lines, if (cursor at x) I type ci" and ci(, nothing happened to 2nd line, but first line changed into:
foo "I"thing else " (I is cursor)
ci( is consistent with ci[, ci{ and cit and all the other <action>i<something>. Only ci' and ci" work like they do. The outliers are the quotes, here, not the brackets.
Vim doesn't consider that quotes come in pairs while brackets do. It has an internal logic for matching pairs that works with actual pairs but not with quotes hence the difference in behavior.
You are not the first to complain about that discrepancy: this is one solution, maybe you can find others.
edit
I don't have a deep knowledge of Vim's internals, unfortunately, so I can only make assumptions, here.
If you ask Vim to do ci" it does its best to find a pair of double quotes but double quotes don't go by pairs: there's no way to tell if a " is the closing one or the opening one contrary to brackets. Because of that, Vim must make some choices. IMO, the choice that would make the most sense considering how the other members of the family work, would be to assume that the cursor is between the quotes and select from the first one to the right to the first one to the left. I can only assume that this method proved wrong in some way or didn't work for some reason and that the other method (the current one) prevailed.
Another explanation could be that the i<something> mechanism is somehow tied to a specific subsystem (maybe the same as showmatch?) that is unable to deal correctly with quotes.
Anyway, just like you, I find this discrepancy weird and I've somehow internalized it and aligned my usage of <action>i" to how the others work. To the point of actually doing 2t"ci" or some variant instead of ci"!! Inefficient, I know.
Did you read :h a'? I completely forgot where I got my "limited understanding" of the issue but it was there! It says:
"Only works within one line. When the cursor starts on a quote, Vim will figure out which quote pairs form a string by searching from the start of the line."
What I get from that is this: for some reasons unknown to us, Vim uses another mechanism for matching quotes than for the other pairs and that is why ci" is different from ciband friends. The underlying cause is not clear at all but I'm fairly certain that the big picture looks a lot like what I imagine.
To me, it looks a lot like a bug or a limitation disguised as a feature.
If you are still curious, I'd suggest you ask any further question on vim-dev.
make use of %
" nnoremap cb cib
nnoremap cb %cib
" nnoremap vb vib
nnoremap vb %vib
nnoremap yb %yib
nnoremap db %dab
to enhance %:
https://github.com/andymass/vim-matchup#tocbar-dopjfd
b can match ( [ {,
Want to use ' to match both ' and "?
nnoremap c' :call DoubleAsSingleC()<CR>
func! DoubleAsSingleC()
" When [!] is added, error messages will also be skipped,
" and commands and mappings will not be aborted
" when an error is detected. |v:errmsg| is still set.
let v:errmsg = ""
silent! :s#\"\([^"]*\)\"#'\1'#g
if (v:errmsg == "")
echo "双变单"
endif
exec "normal ci'"
endfunc
Similar for:
d'
y'
v'
I am having a custom command, in the command a function would be called.
I want to make sure that, the command is executed only if current buffer is in VISUAL mode (v, V or C-V). or, say, the function is executed only if some text in current buffer is currently selected.
visualmode() I cannot use, because it gives the last visual mode type.
I tried putting echo mode() in the function, it gives always n.
and I am not sure, if I selected something, and press : it has already entered command mode, how can I check if the buffer has something in selection right now?
The solution could be easy, but I am stuck here.... can someone shed me some light? thank you.
If you want that kind of functionality, you have to use a mapping (or two different mappings for normal and visual mode).
As you said, as soon as you press :, visual mode is left for command-line mode. However, as the :'<,'> range is automatically inserted, you can pass that range to your function and operate on that. That would allow use from normal mode via an explicit range, too, but this is just sensible and consistent with the built-in commands.
I've been trying to figure out the same problem myself, and I came up with this.
With this autocmd:
autocmd CursorMoved * let s:Mode = mode()
A command like the following that also uses range option -range=0 that alters the <count> argument (0 when no range is passed, other wise the same number as the line the command is called on):
command! -range=0 CmdName call FuncName(<count>, <line1>, <line2>)
Then with logic like this you can check for either a visual selection, a normal range being passed, and no range passed at all:
function! FuncName(count, firstLine, lastLine)
if a:count == 0 "no range given
"do stuff for no given range
else
if s:Mode =~ '\vV|v|' "range was given
"do stuff for visual selection
else
"do stuff for normal range
" w/ a:firstLine & a:lastLine
endif
endif
endfunction
NOTES:
Known Problem - The only case I have found this doesn't work is when a visual selection is only one character ( the cursor wasn't moved), but for most cases this shouldn't be an issue.
Command Note - You have to pass and as arguments rather than using the before call, so a invalid range isn't passed.
I like to have comments like this in my C code:
/********************************************************
* Blah
********************************************************/
But I get tired of typing all those asterisks, and copy/pasting can also get annoying. I was wondering if I could possibly create a macro with Vim so that if I press (for example) CTRL+L, it'll automatically insert that structure in my code and align the cursor in the middle (where the actual comment is written).
Any advice would be appreciated. Thanks!
If you are planning to go on snipmate (which IMHO is a good choice!), you should consider using the version maintained by Garbas on github (pay attention to the required plugins in the readme file).
The version from msanders is no longer maintained.
Since I am new I can't add a comment to the answer above... Sorry
Take a look at snipmate, a vim plugin for TextMate-like snippets.
https://github.com/garbas/vim-snipmate
1min screencast: http://vimeo.com/3535418
snipMate.vim implements some of TextMate's snippets features in Vim. A
snippet is a piece of often-typed text that you can insert into your
document using a trigger word followed by a <tab>.
You can use cvim, and edit ~/.vim/c-support/templates/c.comments.template to your liking.
Heh, just today I was beefing up my own support for this (as these headers are mandated by company coding standard).
iabbrev //== // (96 equal characters)<Enter>//<Enter>// (96 equal characters)<C-O>k
iabbrev //-- // (96 dashes)<Enter>//<Enter>// (96 dashes)<C-O>k
These allow me to type //== and when I press space the whole thing is entered and I'm left with the cursor "in the middle" where I want it.
So- for the OP, if you want to use Ctrl-L, do something like this:
inoremap <C-L> /*****<Enter> * <Enter>*****<C-O>k
(I like abbreviations myself, though...)
Since there are other points in our coding standard where we need to extend dashes and equals out to column 100, I whipped this up today:
iabbrev <expr> === InsertTo99Width('===')
iabbrev <expr> --- InsertTo99Width('---')
function! InsertTo99Width(insertion)
let l:line = getline('.')
let l:linelen = strlen(l:line)
let l:col = col('.')
let l:numchars = (99 - col)
let l:inserted = a:insertion
if (l:linelen <= l:col) && (l:numchars > 0)
let l:numchars += len(l:inserted)
while len(l:inserted) < l:numchars
let l:inserted = l:inserted . l:inserted
endwhile
return l:inserted[0 : l:numchars]
else
return l:inserted
endif
endfunction
(I'm sort of a vim scripting newb so there may be an easier way to make a string of N characters long, but this allows arbitrary strings of arbitrary length to be repeated so if you like dash-space-equal-space or something, it'll still look "nice".)
Anyway finally- you can use <expr> in the inoremap too if you want, so you can make your asterisks go to a set width based on the output of a function. There's a lot of power in there, but it may take some fiddling to get it just the way you want...