From b2d05a24bd541ca32ad77b41c07c13c25528dd6e Mon Sep 17 00:00:00 2001 From: Tom Ryder Date: Sat, 22 Jun 2019 22:37:23 +1200 Subject: Support mapping cancel keys, add Esc as default --- autoload/paste_insert.vim | 44 +++++++++++++++++++++++++++----------------- doc/paste_insert.txt | 5 +++-- 2 files changed, 30 insertions(+), 19 deletions(-) diff --git a/autoload/paste_insert.vim b/autoload/paste_insert.vim index 277f333..6068b51 100644 --- a/autoload/paste_insert.vim +++ b/autoload/paste_insert.vim @@ -43,29 +43,39 @@ function! paste_insert#() abort endfunction -" Key that cancels the pending paste in normal mode, defaults to CTRL-C -let s:cancel = get(g:, 'paste_insert_cancel', '') +" Keys that cancel the pending paste in normal mode, defaults to Ctrl-C and +" Escape +let s:cancel = get(g:, 'paste_insert_cancel', ['', '']) -" Start the paste: save cancel key's prior function, remap it, set 'paste' +" Cache for the prior functions of those keys, in case the user has already +" mapped them +let s:maparg = {} + +" Start the paste: save each cancel key's prior function, remap it, set +" 'paste' function! s:Start() abort - let s:maparg = maparg(s:cancel) - let command = join([ - \ 'nnoremap' - \,s:cancel - \,':doautocmd paste_insert User Cancel' - \]) - execute command + for key in s:cancel + let s:maparg[key] = maparg(key) + let command = join([ + \ 'nnoremap' + \,key + \,':doautocmd paste_insert User Cancel' + \]) + execute command + endfor set paste endfunction " Stop the paste: unset 'paste', restore prior function of cancel key function! s:Stop() abort set nopaste - let command = join( - \ s:maparg !=# '' - \ ? ['nnoremap', s:cancel, s:maparg] - \ : ['nunmap', s:cancel] - \) - execute command - unlet s:maparg + for key in s:cancel + let command = join( + \ s:maparg[key] !=# '' + \ ? ['nnoremap', key, s:maparg[key]] + \ : ['nunmap', key] + \) + execute command + unlet s:maparg[key] + endfor endfunction diff --git a/doc/paste_insert.txt b/doc/paste_insert.txt index 73036e4..ff17437 100644 --- a/doc/paste_insert.txt +++ b/doc/paste_insert.txt @@ -28,8 +28,9 @@ The `(PasteInsert)` map in normal mode just does `:PasteInsert`. OPTIONS *paste_insert-options* *g:paste_insert_cancel* -Set `g:paste_insert_cancel` to the key you want to cancel the pending paste in -normal mode. This defaults to '', for CTRL-C. +Set `g:paste_insert_cancel` to a |List| of the keys you want to cancel the +pending paste in normal mode. This defaults to `['', '']`, for +CTRL-C and Escape. AUTHOR *paste_insert-author* -- cgit v1.2.3 From f4c18d5f6909713e32e0adf7dc7d8e00772806cd Mon Sep 17 00:00:00 2001 From: Tom Ryder Date: Sat, 22 Jun 2019 22:37:45 +1200 Subject: Get normal mode map specifically for cancel --- autoload/paste_insert.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autoload/paste_insert.vim b/autoload/paste_insert.vim index 6068b51..c135f9d 100644 --- a/autoload/paste_insert.vim +++ b/autoload/paste_insert.vim @@ -55,7 +55,7 @@ let s:maparg = {} " 'paste' function! s:Start() abort for key in s:cancel - let s:maparg[key] = maparg(key) + let s:maparg[key] = maparg(key, 'n') let command = join([ \ 'nnoremap' \,key -- cgit v1.2.3 From 182b9687b435553419b769be60324dd41fda3bd8 Mon Sep 17 00:00:00 2001 From: Tom Ryder Date: Sun, 23 Jun 2019 00:40:44 +1200 Subject: More sophisticated map caching This is surprisingly hard. --- autoload/paste_insert.vim | 63 ++++++++++++++++++++++++++++++++++++----------- doc/paste_insert.txt | 7 ++++++ 2 files changed, 55 insertions(+), 15 deletions(-) diff --git a/autoload/paste_insert.vim b/autoload/paste_insert.vim index c135f9d..92fdc54 100644 --- a/autoload/paste_insert.vim +++ b/autoload/paste_insert.vim @@ -49,19 +49,45 @@ let s:cancel = get(g:, 'paste_insert_cancel', ['', '']) " Cache for the prior functions of those keys, in case the user has already " mapped them -let s:maparg = {} +let s:maps = {} + +function! s:MapArg(key, mode) abort + if v:version > 703 || v:version == 703 && has('patch-32') + return maparg(a:key, a:mode, 0, 1) + else + let rhs = maparg(a:key, a:mode) + if rhs ==# '' + return {} + else + return { 'rhs': rhs } + endif + endif +endfunction " Start the paste: save each cancel key's prior function, remap it, set " 'paste' function! s:Start() abort for key in s:cancel - let s:maparg[key] = maparg(key, 'n') - let command = join([ - \ 'nnoremap' - \,key - \,':doautocmd paste_insert User Cancel' - \]) - execute command + let maps = [] + try + let map = s:MapArg(key, 'n') + if !empty(map) + call add(maps, s:MapArg(key, 'n')) + endif + execute 'nunmap '.key + let map = s:MapArg(key, 'n') + if !empty(map) + call add(maps, s:MapArg(key, 'n')) + endif + execute 'nunmap '.key + catch + endtry + if len(maps) == 2 + let maps[0]['buffer'] = 1 + endif + let s:maps[key] = maps + execute 'nnoremap '.key + \.' :doautocmd paste_insert User Cancel' endfor set paste endfunction @@ -70,12 +96,19 @@ endfunction function! s:Stop() abort set nopaste for key in s:cancel - let command = join( - \ s:maparg[key] !=# '' - \ ? ['nnoremap', key, s:maparg[key]] - \ : ['nunmap', key] - \) - execute command - unlet s:maparg[key] + execute 'nunmap '.key + for map in reverse(s:maps[key]) + let command = !has_key(map, 'noremap') || map['noremap'] + \ ? ['nnoremap'] + \ : ['nmap'] + for flag in ['buffer', 'expr', 'nowait', 'silent'] + if has_key(map, flag) && map[flag] + call add(command, '<'.flag.'>') + endif + endfor + call extend(command, [key, map['rhs']]) + execute join(command) + endfor + unlet s:maps[key] endfor endfunction diff --git a/doc/paste_insert.txt b/doc/paste_insert.txt index ff17437..7286c15 100644 --- a/doc/paste_insert.txt +++ b/doc/paste_insert.txt @@ -32,6 +32,13 @@ Set `g:paste_insert_cancel` to a |List| of the keys you want to cancel the pending paste in normal mode. This defaults to `['', '']`, for CTRL-C and Escape. +The plugin will overwrite any existing normal mode maps for these keys during +the normal mode phase of the operation, but will try to restore them back +afterwards. This works well in versions of Vim 7.3.032 or newer, but before +that patch, recursive maps and flags like or are broken, +although should still work. Either pick keys that you don't map, or +even better--upgrade your Vim! + AUTHOR *paste_insert-author* Written and maintained by Tom Ryder . -- cgit v1.2.3 From 43e0185c2ca6edcc961e2cfc510e1067bbe18a32 Mon Sep 17 00:00:00 2001 From: Tom Ryder Date: Sun, 23 Jun 2019 00:41:29 +1200 Subject: Clarify explanation of map settings a bit --- doc/paste_insert.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/paste_insert.txt b/doc/paste_insert.txt index 7286c15..62fa1f8 100644 --- a/doc/paste_insert.txt +++ b/doc/paste_insert.txt @@ -28,9 +28,9 @@ The `(PasteInsert)` map in normal mode just does `:PasteInsert`. OPTIONS *paste_insert-options* *g:paste_insert_cancel* -Set `g:paste_insert_cancel` to a |List| of the keys you want to cancel the -pending paste in normal mode. This defaults to `['', '']`, for -CTRL-C and Escape. +Set `g:paste_insert_cancel` to a |List| of the special codes for keys you want +to cancel the pending paste in normal mode. This defaults to `['', +'']`, for CTRL-C and Escape. The plugin will overwrite any existing normal mode maps for these keys during the normal mode phase of the operation, but will try to restore them back -- cgit v1.2.3 From f93775f827ea791bf7dcc872503b37205c2e4a5a Mon Sep 17 00:00:00 2001 From: Tom Ryder Date: Sun, 23 Jun 2019 01:01:29 +1200 Subject: Clear event table only once so user can use it --- autoload/paste_insert.vim | 6 ++++-- plugin/paste_insert.vim | 5 +++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/autoload/paste_insert.vim b/autoload/paste_insert.vim index 92fdc54..d8585c6 100644 --- a/autoload/paste_insert.vim +++ b/autoload/paste_insert.vim @@ -1,9 +1,11 @@ " Autoloaded entry point function function! paste_insert#() abort - " Set up an event table + " Set up an event table; note that we intentionally don't clear the group. + " We did that in plugin/paste_insert.vim, so that the user could add their + " own hooks if need be. + " augroup paste_insert - autocmd! " Set up the paste and tell the user autocmd User Start diff --git a/plugin/paste_insert.vim b/plugin/paste_insert.vim index a792a50..ecb97ca 100644 --- a/plugin/paste_insert.vim +++ b/plugin/paste_insert.vim @@ -12,6 +12,11 @@ if exists('loaded_paste_insert') || &compatible endif let loaded_paste_insert = 1 +" Create the augroup here, so that the user can hook into it if they need to +augroup paste_insert + autocmd! +augroup END + " Command interface command! -bar PasteInsert \ call paste_insert#() -- cgit v1.2.3 From bf3719012c4ce38a1e0223dd734c01d333be24a2 Mon Sep 17 00:00:00 2001 From: Tom Ryder Date: Sun, 23 Jun 2019 01:02:26 +1200 Subject: Add lots of comments --- autoload/paste_insert.vim | 52 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/autoload/paste_insert.vim b/autoload/paste_insert.vim index d8585c6..421c3a2 100644 --- a/autoload/paste_insert.vim +++ b/autoload/paste_insert.vim @@ -53,6 +53,14 @@ let s:cancel = get(g:, 'paste_insert_cancel', ['', '']) " mapped them let s:maps = {} +" Function to abstract over shortcomings in Vim older than 7.3.032 in yielding +" incomplete map information; return as much as we can in the same structure +" as Vim after this patchlevel does with the {dict} parameter TRUE; +" unfortunately, this is just the mapping's right hand side. We should be +" able to figure out the flag too, but that's it. The documentation +" for this plugin points out this unfortunate caveat, and suggests +" a workaround. +" function! s:MapArg(key, mode) abort if v:version > 703 || v:version == 703 && has('patch-32') return maparg(a:key, a:mode, 0, 1) @@ -69,48 +77,92 @@ endfunction " Start the paste: save each cancel key's prior function, remap it, set " 'paste' function! s:Start() abort + + " Collect and remove existing mappings for this key, replacing with a global + " map that cancels the pending paste operation + " for key in s:cancel let maps = [] + + " We might need to stop partway through this try + + " Collect first mapping, perhaps a buffer-local mapping, since maparg() + " yields those first let map = s:MapArg(key, 'n') if !empty(map) call add(maps, s:MapArg(key, 'n')) endif execute 'nunmap '.key + + " Since we got here, the `:nunmap ` command must have worked, + " and there might be one more global map to collect let map = s:MapArg(key, 'n') if !empty(map) call add(maps, s:MapArg(key, 'n')) endif execute 'nunmap '.key + catch + " Ignore it endtry + + " If we collected two mappings, the first one must have been buffer-local, + " and Vim before 7.3.032 won't have told us that if len(maps) == 2 let maps[0]['buffer'] = 1 endif + + " Add the collected maps to this key's cache let s:maps[key] = maps + + " Set up our map to cancel the pending paste execute 'nnoremap '.key \.' :doautocmd paste_insert User Cancel' endfor + + " Let's go! set paste + endfunction " Stop the paste: unset 'paste', restore prior function of cancel key function! s:Stop() abort + + " Let's stop! set nopaste + + " Now we need to remove our temporary maps, and restore the user's for key in s:cancel + + " Start by getting read of our map; there should now be none at all execute 'nunmap '.key + + " Iterate through any cached maps for this key for map in reverse(s:maps[key]) + + " Start building the command to restore the mapping; assume this is + " a nonrecursive map unless flagged otherwise let command = !has_key(map, 'noremap') || map['noremap'] \ ? ['nnoremap'] \ : ['nmap'] + + " Restore any flags for the mapping as we know them for flag in ['buffer', 'expr', 'nowait', 'silent'] if has_key(map, flag) && map[flag] call add(command, '<'.flag.'>') endif endfor + + " Add the key and right hand call extend(command, [key, map['rhs']]) execute join(command) + endfor + + " Clear the map cache for this key, now that we've restored them unlet s:maps[key] + endfor + endfunction -- cgit v1.2.3 From 30c5c346363945971108896351f1142617ce3022 Mon Sep 17 00:00:00 2001 From: Tom Ryder Date: Sun, 23 Jun 2019 01:02:34 +1200 Subject: Catch specific mapping errors --- autoload/paste_insert.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autoload/paste_insert.vim b/autoload/paste_insert.vim index 421c3a2..e0463a9 100644 --- a/autoload/paste_insert.vim +++ b/autoload/paste_insert.vim @@ -103,7 +103,7 @@ function! s:Start() abort endif execute 'nunmap '.key - catch + catch /^Vim\%((\a\+)\)\=:E31:/ " No such mapping " Ignore it endtry -- cgit v1.2.3 From 8ce0cc5fab4411bbd54210ea70202fb5d1c60842 Mon Sep 17 00:00:00 2001 From: Tom Ryder Date: Sun, 23 Jun 2019 01:03:39 +1200 Subject: Don't fill out .buffer property if already set --- autoload/paste_insert.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autoload/paste_insert.vim b/autoload/paste_insert.vim index e0463a9..f40371b 100644 --- a/autoload/paste_insert.vim +++ b/autoload/paste_insert.vim @@ -109,7 +109,7 @@ function! s:Start() abort " If we collected two mappings, the first one must have been buffer-local, " and Vim before 7.3.032 won't have told us that - if len(maps) == 2 + if len(maps) == 2 && !has_key(maps[0], 'buffer') let maps[0]['buffer'] = 1 endif -- cgit v1.2.3 From 5b66951630ff1c60aa0ef7331a8c9538ca724510 Mon Sep 17 00:00:00 2001 From: Tom Ryder Date: Sun, 23 Jun 2019 01:03:57 +1200 Subject: Flesh out comment a little --- autoload/paste_insert.vim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/autoload/paste_insert.vim b/autoload/paste_insert.vim index f40371b..32b60e4 100644 --- a/autoload/paste_insert.vim +++ b/autoload/paste_insert.vim @@ -108,7 +108,8 @@ function! s:Start() abort endtry " If we collected two mappings, the first one must have been buffer-local, - " and Vim before 7.3.032 won't have told us that + " and Vim before 7.3.032 won't have told us that, so we'll flag it now + " if len(maps) == 2 && !has_key(maps[0], 'buffer') let maps[0]['buffer'] = 1 endif -- cgit v1.2.3 From 114c41dc69884f496c478baf956a75d794de39e7 Mon Sep 17 00:00:00 2001 From: Tom Ryder Date: Sun, 23 Jun 2019 01:16:26 +1200 Subject: Flesh out documentation a lot --- doc/paste_insert.txt | 48 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/doc/paste_insert.txt b/doc/paste_insert.txt index 62fa1f8..d9b2bb2 100644 --- a/doc/paste_insert.txt +++ b/doc/paste_insert.txt @@ -1,4 +1,4 @@ -*paste_insert.txt* For Vim version 7.0 Last change: 2019 Jun 19 +*paste_insert.txt* For Vim version 7.0 Last change: 2019 Jun 23 DESCRIPTION *paste_insert* @@ -8,36 +8,54 @@ after the insert ends, to avoid the annoyances caused by forgetting to do so. It includes a timeout if insert mode isn't entered within 'updatetime' seconds, or if the user navigates away from the buffer or window. It can also -be cancelled with a key in normal mode, by default CTRL-C. +be cancelled with keys in normal mode, by default CTRL-C or Escape. REQUIREMENTS *paste_insert-requirements* This plugin only loads if 'compatible' is not set. +MAPPINGS *paste_insert-mappings* + + *(PasteInsert)* +The `(PasteInsert)` map in normal mode just does `:PasteInsert`. This +is probably the way you want to use this plugin. To use `p`, for +example, you might do this in your |vimrc|: +> + nmap p (PasteInsert) +< COMMANDS *paste_insert-commands* *:PasteInsert* Enter the `:PasteInsert` command just before entering insert mode to set up -the relevant hooks. It takes neither range prefix nor arguments. - -MAPPINGS *paste_insert-mappings* - - *(PasteInsert)* -The `(PasteInsert)` map in normal mode just does `:PasteInsert`. +the relevant hooks. It takes neither range prefix nor arguments. It's made +available for you if you want it, but most users probably just want the +|(PasteInsert)| mapping above. OPTIONS *paste_insert-options* *g:paste_insert_cancel* Set `g:paste_insert_cancel` to a |List| of the special codes for keys you want -to cancel the pending paste in normal mode. This defaults to `['', -'']`, for CTRL-C and Escape. +to cancel the pending paste in normal mode. `['', '']` is the +default, for CTRL-C and Escape. The plugin will overwrite any existing normal mode maps for these keys during -the normal mode phase of the operation, but will try to restore them back -afterwards. This works well in versions of Vim 7.3.032 or newer, but before -that patch, recursive maps and flags like or are broken, -although should still work. Either pick keys that you don't map, or -even better--upgrade your Vim! +the normal mode phase of the operation, but will try to restore them +afterwards. If you don't want your mappings touched at all, set this to the +empty list, and the plugin will leave them alone. + +CAVEATS *paste_insert-caveats* + +By design (Vim's), mappings don't work in paste mode. If you break the insert +with CTRL-C, the |InsertLeave| event doesn't fire, and you'll still be in +'paste' mode, just as you would be without this plugin. Leaving the buffer or +window should fix it. Friends don't let friends use CTRL-C to break insert +mode! + +The map restoration for your chosen cancel keys works well in Vim v7.3.032 or +newer, but before that patch, recursive maps and flags like or +won't be restored correctly, although should still work. If it's +mangling your mappings and you don't want to change keys, your only option is +to upgrade Vim. AUTHOR *paste_insert-author* -- cgit v1.2.3 From 16ae605f844121dd4192297db8cab0528d1af055 Mon Sep 17 00:00:00 2001 From: Tom Ryder Date: Sun, 23 Jun 2019 01:19:45 +1200 Subject: Revert "Clear event table only once so user can... The group clears itself by design once it's done, so they can't use it anyway. --- autoload/paste_insert.vim | 6 ++---- plugin/paste_insert.vim | 5 ----- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/autoload/paste_insert.vim b/autoload/paste_insert.vim index 32b60e4..fe9e482 100644 --- a/autoload/paste_insert.vim +++ b/autoload/paste_insert.vim @@ -1,11 +1,9 @@ " Autoloaded entry point function function! paste_insert#() abort - " Set up an event table; note that we intentionally don't clear the group. - " We did that in plugin/paste_insert.vim, so that the user could add their - " own hooks if need be. - " + " Set up an event table augroup paste_insert + autocmd! " Set up the paste and tell the user autocmd User Start diff --git a/plugin/paste_insert.vim b/plugin/paste_insert.vim index ecb97ca..a792a50 100644 --- a/plugin/paste_insert.vim +++ b/plugin/paste_insert.vim @@ -12,11 +12,6 @@ if exists('loaded_paste_insert') || &compatible endif let loaded_paste_insert = 1 -" Create the augroup here, so that the user can hook into it if they need to -augroup paste_insert - autocmd! -augroup END - " Command interface command! -bar PasteInsert \ call paste_insert#() -- cgit v1.2.3 From 49f773901ff6bf3749c47547afd5aa22b3e4afc2 Mon Sep 17 00:00:00 2001 From: Tom Ryder Date: Sun, 23 Jun 2019 01:24:28 +1200 Subject: Bump VERSION --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 0ea3a94..0d91a54 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.2.0 +0.3.0 -- cgit v1.2.3