Skip to content

the help about "<plug>" mappings could be improved #6705

@lacygoill

Description

@lacygoill

The help about <Plug> mappings (:h using-<Plug>, and various examples given here and there) can all suffer from an issue where the user has to wait before the mapping they pressed takes effect.

Example:

vim9script
set timeoutlen=3000
nmap <F3> <plug>foo
nno <plug>foo    :echo 'foo'<cr>
nno <plug>foobar :echo 'foobar'<cr>

Press <F3> to make Vim echo foo; it will take 3 seconds.

One solution is to wrap the sequence inside a pair of parentheses:

vim9script
set timeoutlen=3000
nmap <F3> <plug>(foo)
nno <plug>(foo)    :echo 'foo'<cr>
nno <plug>(foobar) :echo 'foobar'<cr>

This time, <F3> echo'es foo instantaneously.

This patch updates the documentation so that the users avoid this pitfall when they install <plug> mappings:

diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt
index 45332e6a4..d1c041326 100644
--- a/runtime/doc/usr_41.txt
+++ b/runtime/doc/usr_41.txt
@@ -2044,9 +2044,9 @@ for this mapping, but the user might already use it for something else.  To
 allow the user to define which keys a mapping in a plugin uses, the <Leader>
 item can be used: >
 
- 22	  map <unique> <Leader>a  <Plug>TypecorrAdd
+ 22	  map <unique> <Leader>a  <Plug>(TypecorrAdd)
 
-The "<Plug>TypecorrAdd" thing will do the work, more about that further on.
+The "<Plug>(TypecorrAdd)" thing will do the work, more about that further on.
 
 The user can set the "mapleader" variable to the key sequence that he wants
 this mapping to start with.  Thus if the user has done: >
@@ -2062,15 +2062,15 @@ already happened to exist. |:map-<unique>|
 But what if the user wants to define his own key sequence?  We can allow that
 with this mechanism: >
 
- 21	if !hasmapto('<Plug>TypecorrAdd')
- 22	  map <unique> <Leader>a  <Plug>TypecorrAdd
+ 21	if !hasmapto('<Plug>(TypecorrAdd)')
+ 22	  map <unique> <Leader>a  <Plug>(TypecorrAdd)
  23	endif
 
-This checks if a mapping to "<Plug>TypecorrAdd" already exists, and only
+This checks if a mapping to "<Plug>(TypecorrAdd)" already exists, and only
 defines the mapping from "<Leader>a" if it doesn't.  The user then has a
 chance of putting this in his vimrc file: >
 
-	map ,c  <Plug>TypecorrAdd
+	map ,c  <Plug>(TypecorrAdd)
 
 Then the mapped key sequence will be ",c" instead of "_a" or "\a".
 
@@ -2100,13 +2100,13 @@ function (without the "s:"), which is again another function.
 <SID> can be used with mappings.  It generates a script ID, which identifies
 the current script.  In our typing correction plugin we use it like this: >
 
- 24	noremap <unique> <script> <Plug>TypecorrAdd  <SID>Add
+ 24	noremap <unique> <script> <Plug>(TypecorrAdd)  <SID>Add
  ..
  28	noremap <SID>Add  :call <SID>Add(expand("<cword>"), 1)<CR>
 
 Thus when a user types "\a", this sequence is invoked: >
 
-	\a  ->  <Plug>TypecorrAdd  ->  <SID>Add  ->  :call <SID>Add()
+	\a  ->  <Plug>(TypecorrAdd)  ->  <SID>Add  ->  :call <SID>Add()
 
 If another script was also map <SID>Add, it would get another script ID and
 thus define another mapping.
@@ -2149,9 +2149,10 @@ difference between using <SID> and <Plug>:
 	To make it very unlikely that other plugins use the same sequence of
 	characters, use this structure: <Plug> scriptname mapname
 	In our example the scriptname is "Typecorr" and the mapname is "Add".
-	This results in "<Plug>TypecorrAdd".  Only the first character of
+	This results in "<Plug>(TypecorrAdd)".  Only the first character of
 	scriptname and mapname is uppercase, so that we can see where mapname
-	starts.
+	starts.  The parentheses prevent Vim from waiting for more keys to be
+	typed if another mapping starts in the same way.
 
 <SID>	is the script ID, a unique identifier for a script.
 	Internally Vim translates <SID> to "<SNR>123_", where "123" can be any
@@ -2226,10 +2227,10 @@ Here is the resulting complete example: >
  18		\ synchronization
  19	let s:count = 4
  20
- 21	if !hasmapto('<Plug>TypecorrAdd')
- 22	  map <unique> <Leader>a  <Plug>TypecorrAdd
+ 21	if !hasmapto('<Plug>(TypecorrAdd)')
+ 22	  map <unique> <Leader>a  <Plug>(TypecorrAdd)
  23	endif
- 24	noremap <unique> <script> <Plug>TypecorrAdd  <SID>Add
+ 24	noremap <unique> <script> <Plug>(TypecorrAdd)  <SID>Add
  25
  26	noremenu <script> Plugin.Add\ Correction      <SID>Add
  27
@@ -2279,7 +2280,7 @@ Here is a simple example for a plugin help file, called "typecorr.txt": >
   6	There are currently only a few corrections.  Add your own if you like.
   7
   8	Mappings:
-  9	<Leader>a   or   <Plug>TypecorrAdd
+  9	<Leader>a   or   <Plug>(TypecorrAdd)
  10		Add a correction for the word under the cursor.
  11
  12	Commands:
@@ -2417,13 +2418,13 @@ To make sure mappings will only work in the current buffer use the >
 command.  This needs to be combined with the two-step mapping explained above.
 An example of how to define functionality in a filetype plugin: >
 
-	if !hasmapto('<Plug>JavaImport')
-	  map <buffer> <unique> <LocalLeader>i <Plug>JavaImport
+	if !hasmapto('<Plug>(JavaImport)')
+	  map <buffer> <unique> <LocalLeader>i <Plug>(JavaImport)
 	endif
-	noremap <buffer> <unique> <Plug>JavaImport oimport ""<Left><Esc>
+	noremap <buffer> <unique> <Plug>(JavaImport) oimport ""<Left><Esc>
 
 |hasmapto()| is used to check if the user has already defined a map to
-<Plug>JavaImport.  If not, then the filetype plugin defines the default
+<Plug>(JavaImport).  If not, then the filetype plugin defines the default
 mapping.  This starts with |<LocalLeader>|, which allows the user to select
 the key(s) he wants filetype plugin mappings to start with.  The default is a
 backslash.
@@ -2440,12 +2441,12 @@ plugin for the mail filetype: >
 	" Add mappings, unless the user didn't want this.
 	if !exists("no_plugin_maps") && !exists("no_mail_maps")
 	  " Quote text by inserting "> "
-	  if !hasmapto('<Plug>MailQuote')
-	    vmap <buffer> <LocalLeader>q <Plug>MailQuote
-	    nmap <buffer> <LocalLeader>q <Plug>MailQuote
+	  if !hasmapto('<Plug>(MailQuote)')
+	    vmap <buffer> <LocalLeader>q <Plug>(MailQuote)
+	    nmap <buffer> <LocalLeader>q <Plug>(MailQuote)
 	  endif
-	  vnoremap <buffer> <Plug>MailQuote :s/^/> /<CR>
-	  nnoremap <buffer> <Plug>MailQuote :.,$s/^/> /<CR>
+	  vnoremap <buffer> <Plug>(MailQuote) :s/^/> /<CR>
+	  nnoremap <buffer> <Plug>(MailQuote) :.,$s/^/> /<CR>
 	endif
 
 Two global variables are used:

See here for an example of a user who was faced with this issue.


Note that only the closing parenthesis is necessary. The opening one is just there for symmetry and making the mappings a little more readable. Also, any character would work, as long as it's not commonly used in the sequence which follows <plug>. Usually, such a sequence only uses alphabetical characters (maybe digits, underscores and hyphens too), but not parentheses. It seems that the convention which is followed nowadays by most plugins authors is to use parentheses.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions