diff options
-rw-r--r-- | VERSION | 2 | ||||
-rw-r--r-- | autoload/paste_insert.vim | 130 | ||||
-rw-r--r-- | doc/paste_insert.txt | 46 |
3 files changed, 150 insertions, 28 deletions
@@ -1 +1 @@ -0.2.0 +0.3.0 diff --git a/autoload/paste_insert.vim b/autoload/paste_insert.vim index 277f333..fe9e482 100644 --- a/autoload/paste_insert.vim +++ b/autoload/paste_insert.vim @@ -43,29 +43,125 @@ 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', '<C-C>') +" Keys that cancel the pending paste in normal mode, defaults to Ctrl-C and +" Escape +let s:cancel = get(g:, 'paste_insert_cancel', ['<C-C>', '<Esc>']) -" 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: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 <buffer> 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) + 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 - let s:maparg = maparg(s:cancel) - let command = join([ - \ 'nnoremap' - \,s:cancel - \,':<C-U>doautocmd paste_insert User Cancel<CR>' - \]) - execute command + + " 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 <buffer> '.key + + " Since we got here, the `:nunmap <buffer>` 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 /^Vim\%((\a\+)\)\=:E31:/ " No such mapping + " 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, so we'll flag it now + " + if len(maps) == 2 && !has_key(maps[0], 'buffer') + 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 + \.' :<C-U>doautocmd paste_insert User Cancel<CR>' + 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 - let command = join( - \ s:maparg !=# '' - \ ? ['nnoremap', s:cancel, s:maparg] - \ : ['nunmap', s:cancel] - \) - execute command - unlet s:maparg + + " 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 diff --git a/doc/paste_insert.txt b/doc/paste_insert.txt index 73036e4..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,28 +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* + + *<Plug>(PasteInsert)* +The `<Plug>(PasteInsert)` map in normal mode just does `:PasteInsert`. This +is probably the way you want to use this plugin. To use `<Leader>p`, for +example, you might do this in your |vimrc|: +> + nmap <Leader>p <Plug>(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* - - *<Plug>(PasteInsert)* -The `<Plug>(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 +|<Plug>(PasteInsert)| mapping above. 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 '<C-C>', for CTRL-C. +Set `g:paste_insert_cancel` to a |List| of the special codes for keys you want +to cancel the pending paste in normal mode. `['<C-C>', '<Esc>']` 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 +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 <expr> or <silent> +won't be restored correctly, although <buffer> 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* |