sponsor Vim development Vim logo Vim Book Ad

repeatableMapping : Set up mappings that can be repeated via repeat.vim.

 script karma  Rating 0/0, Downloaded by 1997  Comments, bugs, improvements  Vim wiki

created by
Ingo Karkat
 
script type
utility
 
description
DESCRIPTION
This plugin simplifies the application of the repeat.vim and visualrepeat.vim
repeat functionality to existing mappings. As with repeat.vim, all
embellishments are optional; if the corresponding plugin isn't available, the
call will be ignored and the original mappings persist unchanged (though
without any repeat functionality).

RETROFIT MAPPING REPETITION
Some plugins provide out-of-the-box support for repeat.vim, many do not.
Some mappings have been developed in an ad-hoc fashion, e.g. as custom
filetype-specific workflow shortcuts, and therefore do not have the
canonical remapping layer of <Plug> mappings (which repeat.vim requires).

This plugin allows to retrofit repeatability to mappings from plugins that
do not directly use repeat.vim. It also offers a succinct way for custom
mappings to acquire repeatability, and spares them the definition of
<Plug>-mappings and adding the repeat#set() boilerplate code themselves.

VISUAL MODE REPETITION
repeat.vim only supports normal mode repetition, as this is the only
repetition that is built into Vim. However, one can define repetition in
visual mode:
    xnoremap . :normal .<CR>
or with pass-through of [count]:
    xnoremap . :execute 'normal' (v:count ? v:count : '') . '.'<CR>
(Or better a more fancy version that only applies the repeat command over
entire lines in linewise visual mode, keeps the current cursor position in
characterwise visual mode, and does nothing (sensible) in blockwise visual
mode. The visualrepeat.vim plugin provides this.)

The <Plug>(ReenterVisualMode) mapping allows to apply repeat.vim repetition to
visual mode commands; just prepend it in the call to repeat#set():
    vnoremap <silent> <Plug>MyMapping ...:silent! call repeat#set("\<Plug>(ReenterVisualMode)\<Plug>MyMapping")<CR>

If a previously used visual mode mapping is repeated from an active visual
selection, that selection is the affected area for the repetition. The
recorded repeat count is passed to the mapping.

If repetition is invoked in normal mode, the repetition works on a new
selection, depending on the last visual mode:
- For linewise selections, the repetition selects the current line.
- For characterwise selections, a new selection starting at the cursor
  position, with the same size as the last visual selection is used. 1v
A [count] is passed to the mapping and overrides the recorded one.

If a non-visual mode mapping is repeated from an active visual selection, it
won't (can't) apply to the visual selection, but to whatever that mapping
typically applies. So, you can only repeat previous visual mode mappings on
a visual selection! The only way around this is to cross-link both mapping
types, and to use a more intelligent version of the repeat definition in
visual mode. This is described next:

CROSS-REPEAT
Through the visualrepeat.vim plugin, it is possible to make normal mode and
visual mode mappings repeat each other, with the normal mode mapping repeated
over the visual selection, and the visual mode mapping repeated over a new
selection, such as:
- For previous linewise selections, the repetition applies to the same amount
  of lines, unless a different [count] is given. In that case, it selects
  [count] lines starting at the cursor position.
- Else, a new selection starting at the cursor position, with the same size as
  the last visual selection (times [count], if given and different) is used.
  1v
The mapping itself is always repeated with the recorded [count], not a new
given one. For cross-repeat of a visual mode mapping in normal mode, a new
different [count] only affects the range it applies to!

For cross-repeat, use the repeatableMapping#makeCrossRepeatable() function,
for single normal mode and visual mode mappings, and
repeatableMapping#makeMultipleCrossRepeatable() for multiple normal mode and
a single visual mode mapping.
For existing <Plug>-mappings, there's the
repeatableMapping#makeMultiplePlugMappingCrossRepeatable() function.

If the repeat needs to go to a different <Plug>-mapping (for example when you
have a mapping that queries the user, and you want to avoid the query on
repeat through a non-interactive mapping variant that recalls the saved
original query's value), use the
repeatableMapping#makePlugMappingWithDifferentRepeatCrossRepeatable()
function. With this variant of repeatableMapping#makeCrossRepeatable(), you
can specify separate repeat <Plug>-mappings:
    silent! call repeatableMapping#makePlugMappingWithDifferentRepeatCrossRepeatable(
    \   'nnoremap', '<Plug>(FooRange)',           '<Plug>(FooRangeRepeat)',
    \   'xnoremap', '<Plug>(FooSelection)',       '<Plug>(FooSelectionRepeat)'
    \)
    silent! call repeatableMapping#makePlugMappingWithDifferentRepeatCrossRepeatable(
    \   'nnoremap', '<Plug>(FooRangeRepeat)',     '<Plug>(FooRangeRepeat)',
    \   'xnoremap', '<Plug>(FooSelectionRepeat)', '<Plug>(FooSelectionRepeat)'
    \)

USAGE
RETROFIT MAPPING REPETITION
First define your mapping in the usual way:
    nnoremap <silent> <Leader>v :version<CR>
Then make the mapping repeatable; this will redefine the original mapping
and insert the call to repeat#set() at the end.
    silent! call repeatableMapping#makeRepeatable('nnoremap', '<Leader>v', 'ShowVersion')

The processing requires the following information:
- The original mapping command (for the map-mode, whether :noremap or not,
  any optional map-arguments like "<buffer>"; "<silent>" is added
  automatically to avoid showing the repeat invocation).
- The mapping's {lhs} (i.e. keys that invoke the mapping).
- A unique name for the intermediate <Plug>-mapping that needs to be created
  so that repeat.vim can invoke it.
- Optional: The default count that will be prefixed to the mapping if no
  explicit numeric argument was given; i.e. the second optional argument of
  repeat#set(). If your mapping doesn't accept a numeric argument and you
  never want to receive one, pass a value of -1.
Note that the wrapped Ex command in the mapping must support command
sequencing with | (i.e. custom commands must be defined with :command-bar),
as the repeat insertion will be appended with <Bar>. If that is not the case,
wrap the command in :execute.

If you already have a <Plug> mapping:
    vnoremap <silent> <Plug>ShowVersion :<C-U>version<CR>
    vmap <Leader>v <Plug>ShowVersion
You can redefine the <Plug> mapping directly, and just have the call to
repeat#set() appended:
    silent! call repeatableMapping#makePlugMappingRepeatable('vnoremap', '<Plug>ShowVersion')
This is especially useful to retrofit existing visual mode mappings with the
ability to be repeatable, in conjunction with the visual mode repetition
described above.

VISUAL MODE REPETITION
First define your normal and visual mode mappings:
    nnoremap <buffer> <LocalLeader>qq :s/^/> /<CR>
    vnoremap <buffer> <LocalLeader>q  :s/^/> /<CR>

You could now make both mappings repeatable via two separate calls to
repeatableMapping#makeRepeatable(), as described in the above section, but
with that, the mappings won't repeat each other. Therefore, it is recommended
to use repeatableMapping#makeCrossRepeatable(), so that the normal mode
mapping can be repeated over a visual selection and vice versa:
    silent! call repeatableMapping#makeCrossRepeatable(
    \   'nnoremap <buffer>', '<LocalLeader>qq', 'QuoteLine',
    \   'vnoremap <buffer>', '<LocalLeader>q',  'QuoteSelection'
    \)

Again, if you already have <Plug> mappings (and need them for a configurable
plugin), you can use the alternative function:
    silent! call repeatableMapping#makePlugMappingCrossRepeatable(
    \   'nnoremap <buffer>', '<Plug>QuoteLine',
    \   'vnoremap <buffer>', '<Plug>QuoteSelection'
    \)
Note that you need to do this _after_ any
    if ! hasmapto('<Plug>QuoteLine', 'n') ...
default mapping tests; because the <Plug>-mapping is contained in the
retrofitted mapping, the test would otherwise always be false, and the default
mappings would be missing!
 
install details
INSTALLATION
The code is hosted in a Git repo at
    https://github.com/inkarkat/vim-repeatableMapping
You can use your favorite plugin manager, or "git clone" into a directory used
for Vim packages. Releases are on the "stable" branch, the latest unstable
development snapshot on "master".

This script is also packaged as a vimball. If you have the "gunzip"
decompressor in your PATH, simply edit the *.vmb.gz package in Vim; otherwise,
decompress the archive first, e.g. using WinZip. Inside Vim, install by
sourcing the vimball or via the :UseVimball command.
    vim repeatableMapping*.vmb.gz
    :so %
To uninstall, use the :RmVimball command.

DEPENDENCIES
- Requires Vim 7.0 or higher.
- repeat.vim (vimscript #2136) plugin (optional, but without it this plugin
  doesn't provide any functionality).
- visualrepeat.vim (vimscript #3848 plugin) (version 2.00 or higher; optional,
  but recommended for cross-repeat functionality).
 

rate this script Life Changing Helpful Unfulfilling 
script versions (upload new version)

Click on the package to download.

package script version date Vim version user release notes
repeatableMapping-2.20.vmb.gz 2.20 2020-04-04 7.0 Ingo Karkat - ENH: All functions now support a second optional a:isRepeatRegister argument (to skip specifying a a:defaultCount, pass an empty String as the first optional argument) that if set invokes repeat#setreg() before the original {rhs} and also tweaks the repeat mappings that recreate a visual selection (which clobber count and also register) to save and restore the repeated register for the repeat as well.
repeatableMapping-2.11.vmb.gz 2.11 2019-07-25 7.0 Ingo Karkat - Documentation: Mention that a <silent> map-argument does not need to be passed; I was confused about this myself.
- FIX: The ingo-library may not be loaded yet (as repeatable mappings are usually defined early in plugin scripts); try loading it before testing for its existence.
- Escaping (duplicated from ingo#compat#maparg()) didn't consider <; in fact, it needs to escape stand-alone < and escaped \<, but not proper key notations like <C-CR>.
repeatableMapping-2.10.vmb.gz 2.10 2015-03-08 7.0 Ingo Karkat - ENH: Allow to pass Funcref as a:defaultCount; the value will then be determined via a dynamic lookup. This is necessary for mappings that clobber v:count (e.g. due to use of :normal). They can now save the original count and pass it on to the repeat part via the Funcref, which is more elegant than a global variable.
repeatableMapping-2.01.vmb.gz 2.01 2014-11-20 7.0 Ingo Karkat - BUG: Typo in repeatableMapping#makePlugMappingCrossRepeatable() causes "E116: Invalid arguments for function call", which unfortunately usually is suppressed by the :silent! invocation.
repeatableMapping-2.00.vmb.gz 2.00 2013-11-22 7.0 Ingo Karkat Initial upload
ip used for rating: 54.225.1.66

If you have questions or remarks about this site, visit the vimonline development pages. Please use this site responsibly.
Questions about Vim should go to the maillist. Help Bram help Uganda.
   
Vim at Github