diff options
author | Tom Ryder <tom@sanctum.geek.nz> | 2018-07-12 01:12:05 +1200 |
---|---|---|
committer | Tom Ryder <tom@sanctum.geek.nz> | 2018-07-12 01:14:46 +1200 |
commit | 6d28047f5b515558e592f4765e6113bfe6f5e555 (patch) | |
tree | 5d7389085a3da1cae1af7e5c6d334096b283cc0f | |
parent | Refactor completely with TextChanged/CursorMoved (diff) | |
download | vim-insert-cancel-6d28047f5b515558e592f4765e6113bfe6f5e555.tar.gz vim-insert-cancel-6d28047f5b515558e592f4765e6113bfe6f5e555.zip |
Complete overhaul
If it's not working now, I don't know what to do...
-rw-r--r-- | doc/insert_cancel.txt | 24 | ||||
-rw-r--r-- | plugin/insert_cancel.vim | 71 |
2 files changed, 54 insertions, 41 deletions
diff --git a/doc/insert_cancel.txt b/doc/insert_cancel.txt index 5893a05..d6efbfa 100644 --- a/doc/insert_cancel.txt +++ b/doc/insert_cancel.txt @@ -10,22 +10,14 @@ something useful, and to fire |InsertLeave| afterwards where available. REQUIREMENTS *insert_cancel-requirements* This plugin is only available if 'compatible' is not set. It works better on -newer versions of Vim. - -If you have |autocmd| and at least |version-7.4| (or |version-7.3| with patch -867) it works really well, because it has |TextChanged| to lean on for -watching |b:changedtick|. - -If you have |autocmd| and |version-7.0| to |version-7.3|, it leans on -|CursorMoved| instead. This still seems pretty good, but I imagine it's more -likely there are subtle gaps, and it's also not ideal in terms of performance -because the event fires a lot without doing anything useful. - -If you have just |version6|, all the plugin has to go on are the |'[| and |']| -marks. It still kind of works, but there are some big gaps; it won't undo text -that was erased when insert mode was entered with |c| or |s| or its variants. -It also may not undo new blank lines. Even this is still slightly better than -just the intuitive mapping: +newer versions of Vim. It works best if you have |autocmd| and at least +|version-7.0|, because it leans on |CursorMoved|. + +If you don't have |autocmd| and |version7|, all the plugin has to go on are +the |'[| and |']| marks. It still kind of works, but there are some big gaps; +it won't undo text that was erased when insert mode was entered with |c| or +|s| or its variants, and it also may not undo new blank lines. Even this is +still rather better than just the intuitive mapping: > imap <C-C> <Esc>u < diff --git a/plugin/insert_cancel.vim b/plugin/insert_cancel.vim index 61d0b49..51216d0 100644 --- a/plugin/insert_cancel.vim +++ b/plugin/insert_cancel.vim @@ -3,6 +3,8 @@ " change upon insert exit, if we made a change; intended for remapping " insert-mode Ctrl-C to do something useful. " +" This was *way* harder to figure out than it looks. +" " Author: Tom Ryder <tom@sanctum.geek.nz> " License: Same as Vim itself " @@ -14,38 +16,57 @@ if v:version < 600 endif let g:loaded_insert_cancel = 1 -" Initialise s:changedtick so vint understands -let s:changedtick = 0 +" On leaving insert mode, whether normally or via <Plug>InsertCancel, check if +" changenr() exceeds the last time we cached it, and flag that a change has +" taken place if it did +function! s:Check() + if changenr() > b:insert_cancel_changenr + let b:insert_cancel_changed = 1 + endif +endfunction -" Set up an appropriate hook to track b:changedtick before we hit InsertLeave -if has('autocmd') - augroup insert_cancel - autocmd! +" On entering insert mode, reset the changed flag and check for a new round of +" changes since insert mode was opened +function! s:Enter() + let b:insert_cancel_changed = 0 + call s:Check() +endfunction - " Ideal; only runs when the text actually changes - if v:version > 703 || v:version == 703 && has('patch867') - autocmd TextChanged * let s:changedtick = b:changedtick +" On cancelling insert mode, if we think we made a change, undo it +function! s:Cancel() - " Workable but wasteful and not quite as correct; updates every move - elseif v:version >= 700 - autocmd CursorMoved * let s:changedtick = b:changedtick + " The flag exists, if it's on, undo + if exists('b:insert_cancel_changed') + if b:insert_cancel_changed + silent undo endif - augroup END -endif - -" Try hard to figure out whether we made a change to undo, and undo it if so -function! s:InsertCancel() - if !&modified - return - endif - if s:changedtick > 0 && b:changedtick > s:changedtick - \ || line("'[") != line("']") - \ || col("'[") != col("']") + " The flag didn't exist, fall back to marks; if the line number or column + " number of the marks for the last changed text aren't exactly equal, that + " suggests we changed something; undo it + elseif line("'[") != line("']") || col("'[") != col("']") silent undo endif + endfunction -" Provide plugin mapping +" Set up the hooks described for the functions above, if Vim is new enough to +" support all the hooks required +if has('autocmd') && v:version >= 700 + augroup insert_cancel + autocmd! + + " On buffer load and cursor move, cache the current change number + autocmd BufNewFile,BufRead,CursorMoved * + \ let b:insert_cancel_changenr = changenr() + + " Function wrappers for entering and leaving insert mode + autocmd InsertEnter * call s:Enter() + autocmd InsertLeave * call s:Check() + + augroup END +endif + +" Mapping that exits insert mode normally and checks for a change to undo inoremap <silent> <Plug>InsertCancel - \ <Esc>:<C-U>call <SID>InsertCancel()<CR> + \ <Esc>:<C-U>call <SID>Cancel()<CR> |