aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--VERSION2
-rw-r--r--autoload/paste_insert.vim130
-rw-r--r--doc/paste_insert.txt46
3 files changed, 150 insertions, 28 deletions
diff --git a/VERSION b/VERSION
index 0ea3a94..0d91a54 100644
--- a/VERSION
+++ b/VERSION
@@ -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*