Prioritize buffer-local mappings over global ones (with patch)

50 views
Skip to first unread message

Michael Henry

unread,
Jan 29, 2013, 8:28:08 AM1/29/13
to vim...@googlegroups.com
All,

Andy Wokula suggested a solution to the problem that
buffer-local mappings have to wait if they are prefixes of
global mappings. Below is a patch that implements his
suggestion.

A new 'localmaplinger' option is introduced. When set (the
default), Vim behaves in the traditional way. When reset,
complete buffer-local mappings will be accepted immediately
without waiting for incomplete global mappings.

As I'd commented in a previous thread, it's hard to know what
name to choose for this option; improvements to the name are
welcome.

The patch includes documentation along with the minor code
change. I didn't know how to implement a test for this
functionality. If anyone has a suggestion on that front, I'd be
happy to try to implement something.

Michael Henry

diff -r 274c841f033a runtime/doc/map.txt
--- a/runtime/doc/map.txt Fri Jan 25 20:11:01 2013 +0100
+++ b/runtime/doc/map.txt Tue Jan 29 08:26:29 2013 -0500
@@ -654,6 +654,15 @@
you type slowly, or your system is slow, reset the 'timeout' option.
Then you
might want to set the 'ttimeout' option.

+ *map-linger*
+By default, the presence of any incomplete matches will cause Vim to
wait for
+more input, as described above in |map-typing|. This can be undesirable
+when a short buffer-local mapping is a prefix of a longer global mapping,
+since it's likely that the user wants the specialized local mapping to
+be used right away. To cause Vim to accept complete buffer-local mappings
+immediately despite the presence of incomplete global mappings, reset the
+'localmaplinger' option.
+
*map-keys-fails*
There are situations where key codes might not be recognized:
- Vim can only read part of the key code. Mostly this is only the first
diff -r 274c841f033a runtime/doc/options.txt
--- a/runtime/doc/options.txt Fri Jan 25 20:11:01 2013 +0100
+++ b/runtime/doc/options.txt Tue Jan 29 08:26:29 2013 -0500
@@ -4649,6 +4649,26 @@
Note that using the "-u NONE" and "--noplugin" command line arguments
reset this option. |-u| |--noplugin|

+ *'localmaplinger'* *'nolocalmaplinger'*
+'localmaplinger' boolean (default on)
+ global
+ {not in Vi}
+ When 'localmaplinger' is set (the default), Vim gives equal
priority
+ to buffer-local and global mappings in the traditional way. As
+ explained in |map-typing|, it compares what you type against all
+ mapped sequences. If it finds at least one incomplete match,
it will
+ get more characters until no more incomplete matches exist,
then use
+ the longest complete match it has found (if any).
+
+ When 'localmaplinger' is reset, Vim will treat buffer-local
mappings
+ as more important than global mappings. When it finds a complete
+ match for a buffer-local mapping with no incomplete buffer-local
+ matches, it will not wait for any incomplete matches of global
+ mappings. This is useful for plugins that make buffer-local
mappings
+ which are prefixes of longer global mappings, resulting in forced
+ delays of 'timeoutlen' before the incomplete global mapping
times out
+ and allows the local mapping to take effect.
+
*'macatsui'* *'nomacatsui'*
'macatsui' boolean (default on)
global
diff -r 274c841f033a src/getchar.c
--- a/src/getchar.c Fri Jan 25 20:11:01 2013 +0100
+++ b/src/getchar.c Tue Jan 29 08:26:29 2013 -0500
@@ -1912,6 +1912,7 @@
mapblock_T *mp;
#ifdef FEAT_LOCALMAP
mapblock_T *mp2;
+ int expecting_global_mappings;
#endif
mapblock_T *mp_match;
int mp_match_len = 0;
@@ -2093,6 +2094,7 @@
/* First try buffer-local mappings. */
mp = curbuf->b_maphash[MAP_HASH(local_State, c1)];
mp2 = maphash[MAP_HASH(local_State, c1)];
+ expecting_global_mappings = (mp && mp2);
if (mp == NULL)
{
mp = mp2;
@@ -2116,6 +2118,22 @@
#endif
(mp = mp->m_next))
{
+#ifdef FEAT_LOCALMAP
+ if (expecting_global_mappings && mp2 == NULL)
+ {
+ /*
+ * This is the first global mapping. If we've
+ * got a complete buffer-local match and we
+ * shouldn't linger for a longer global match,
+ * use the current match.
+ */
+ if (mp_match && !p_lmlinger)
+ {
+ break;
+ }
+ expecting_global_mappings = FALSE;
+ }
+#endif
/*
* Only consider an entry if the first character
* matches and it is for the current state.
diff -r 274c841f033a src/option.c
--- a/src/option.c Fri Jan 25 20:11:01 2013 +0100
+++ b/src/option.c Tue Jan 29 08:26:29 2013 -0500
@@ -1706,6 +1706,11 @@
{"loadplugins", "lpl", P_BOOL|P_VI_DEF,
(char_u *)&p_lpl, PV_NONE,
{(char_u *)TRUE, (char_u *)0L} SCRIPTID_INIT},
+#ifdef FEAT_LOCALMAP
+ {"localmaplinger",NULL, P_BOOL|P_VI_DEF,
+ (char_u *)&p_lmlinger, PV_NONE,
+ {(char_u *)TRUE, (char_u *)0L} SCRIPTID_INIT},
+#endif
#ifdef FEAT_GUI_MAC
{"macatsui", NULL, P_BOOL|P_VI_DEF|P_RCLR,
(char_u *)&p_macatsui, PV_NONE,
diff -r 274c841f033a src/option.h
--- a/src/option.h Fri Jan 25 20:11:01 2013 +0100
+++ b/src/option.h Tue Jan 29 08:26:29 2013 -0500
@@ -592,6 +592,9 @@

EXTERN int p_lz; /* 'lazyredraw' */
EXTERN int p_lpl; /* 'loadplugins' */
+#ifdef FEAT_LOCALMAP
+EXTERN int p_lmlinger; /* 'localmaplinger' */
+#endif
#ifdef FEAT_GUI_MAC
EXTERN int p_macatsui; /* 'macatsui' */
#endif

Andy Wokula

unread,
Jan 29, 2013, 8:55:01 AM1/29/13
to vim...@googlegroups.com
Am 29.01.2013 14:28, schrieb Michael Henry:
> All,
>
> Andy Wokula suggested a solution to the problem that
> buffer-local mappings have to wait if they are prefixes of
> global mappings. Below is a patch that implements his
> suggestion.
>
> A new 'localmaplinger' option is introduced. When set (the
> default), Vim behaves in the traditional way. When reset,
> complete buffer-local mappings will be accepted immediately
> without waiting for incomplete global mappings.
>
> As I'd commented in a previous thread, it's hard to know what
> name to choose for this option; improvements to the name are
> welcome.

Interesting! My attention stopped at the option name though ...
Other suggestions:
'bufmapsonly' 'bmo' with default off
'hideglobalmaps' 'hgm' with default off
'disableglobalmaps'
('noglobalmaps' <= maybe the option should not start with "no")
...

I think turning the option *on* should make a difference.

--
Andy

Christian Brabandt

unread,
Jan 29, 2013, 9:32:47 AM1/29/13
to vim...@googlegroups.com
Or perhaps 'priorizelocalmaps' (plm) or 'localmappriority' (lmp)?

regards,
Christian

Andy Wokula

unread,
Jan 29, 2013, 12:40:38 PM1/29/13
to vim...@googlegroups.com
Sorry, I didn't read the patch :-/
Ok, global mappings will still be available!

Hmm, I think "priority" in the name is slightly ... misleading.

Other suggestions:
'onlywaitforbufmaps'
'solebufmapswait'

'solelocaltimeout'
'localtimeoutonly'
'localtimeout' 'ltm'
'buflocaltimeout' 'blt'

...

--
Andy

Bram Moolenaar

unread,
Jan 29, 2013, 4:33:41 PM1/29/13
to Michael Henry, vim...@googlegroups.com

Michael Henry wrote:

> Andy Wokula suggested a solution to the problem that
> buffer-local mappings have to wait if they are prefixes of
> global mappings. Below is a patch that implements his
> suggestion.
>
> A new 'localmaplinger' option is introduced. When set (the
> default), Vim behaves in the traditional way. When reset,
> complete buffer-local mappings will be accepted immediately
> without waiting for incomplete global mappings.
>
> As I'd commented in a previous thread, it's hard to know what
> name to choose for this option; improvements to the name are
> welcome.
>
> The patch includes documentation along with the minor code
> change. I didn't know how to implement a test for this
> functionality. If anyone has a suggestion on that front, I'd be
> happy to try to implement something.

I do not like the behavior to depend on timing. I would prefer the
local mapping to always take precedence, also when another character was
already typed that causes a global mapping to match. Otherwise the
behavior depends on how busy your computer is, or the remote connection.

Even better would be if we do not need an option at all. Every option
makes it more difficult for a Vim user to understand what's happening.

So would it be "the right thing to do" to always let local mappings
overrule any matching global mapping? It's a slightly incompatible
change though. But the current behavior is bad enough to accept that
(you need to type another chacter to stop waiting for the possibility
that the global mapping would match).


--
With sufficient thrust, pigs fly just fine.
-- RFC 1925

/// Bram Moolenaar -- Br...@Moolenaar.net -- http://www.Moolenaar.net \\\
/// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\ an exciting new programming language -- http://www.Zimbu.org ///
\\\ help me help AIDS victims -- http://ICCF-Holland.org ///

Andy Wokula

unread,
Jan 29, 2013, 6:31:52 PM1/29/13
to vim...@googlegroups.com
Am 29.01.2013 22:33, schrieb Bram Moolenaar:>
> Michael Henry wrote:
>
>> Andy Wokula suggested a solution to the problem that
>> buffer-local mappings have to wait if they are prefixes of
>> global mappings. Below is a patch that implements his
>> suggestion.
>>
>> A new 'localmaplinger' option is introduced. When set (the
>> default), Vim behaves in the traditional way. When reset,
>> complete buffer-local mappings will be accepted immediately
>> without waiting for incomplete global mappings.
>>
>> As I'd commented in a previous thread, it's hard to know what
>> name to choose for this option; improvements to the name are
>> welcome.
>>
>> The patch includes documentation along with the minor code
>> change. I didn't know how to implement a test for this
>> functionality. If anyone has a suggestion on that front, I'd be
>> happy to try to implement something.
>
> I do not like the behavior to depend on timing. I would prefer the
> local mapping to always take precedence, also when another character
> was already typed that causes a global mapping to match. Otherwise
> the behavior depends on how busy your computer is, or the remote
> connection.

"Solution": another option 'shorttimeoutlen', next to 'timeoutlen' and
'ttimeoutlen'. Defines how long to wait for an "atomic" key sequence.
Ok, just kidding, this is crap.

> Even better would be if we do not need an option at all. Every option
> makes it more difficult for a Vim user to understand what's happening.

No problem with that!

> So would it be "the right thing to do" to always let local mappings
> overrule any matching global mapping? It's a slightly incompatible
> change though. But the current behavior is bad enough to accept that
> (you need to type another character to stop waiting for the
> possibility that the global mapping would match).

I think it's the right thing.

Then one can no longer map reliably to the {lhs} of a global mapping,
but it's a good habit anyway to always map to the {rhs}.

--
Andy

Michael Henry

unread,
Jan 30, 2013, 9:14:46 AM1/30/13
to Bram Moolenaar, vim...@googlegroups.com
On 01/29/2013 04:33 PM, Bram Moolenaar wrote:
> I do not like the behavior to depend on timing. I would prefer the
> local mapping to always take precedence, also when another character was
> already typed that causes a global mapping to match. Otherwise the
> behavior depends on how busy your computer is, or the remote connection.

Agreed. The patch eliminates the dependence on timing, which I
see as a good thing, too.

> Even better would be if we do not need an option at all. Every option
> makes it more difficult for a Vim user to understand what's happening.

Agreed.

> So would it be "the right thing to do" to always let local mappings
> overrule any matching global mapping? It's a slightly incompatible
> change though. But the current behavior is bad enough to accept that
> (you need to type another chacter to stop waiting for the possibility
> that the global mapping would match).

I'd be in favor of making the behavior non-optional. Here is a
modification to the patch that removes the option.

Michael Henry

diff -r 274c841f033a runtime/doc/map.txt
--- a/runtime/doc/map.txt Fri Jan 25 20:11:01 2013 +0100
+++ b/runtime/doc/map.txt Wed Jan 30 09:13:18 2013 -0500
@@ -654,6 +654,18 @@
you type slowly, or your system is slow, reset the 'timeout' option.
Then you
might want to set the 'ttimeout' option.

+ *map-precedence*
+Buffer-local mappings (defined using |:map-<buffer>|) take precedence over
+global mappings. When a buffer-local mapping is the same as a global
mapping,
+Vim will use the buffer-local mapping. In addition, Vim will use a
complete
+buffer-local mapping immediately, even if a longer global mapping has the
+buffer-local mapping as a prefix. For example, given the following two
+mappings: >
+ :map <buffer> \a :echo "Local \a"<CR>
+ :map \abc :echo "Global \abc"<CR>
+The buffer-local mapping \a will be used immediately. Vim will not
+wait for more characters to see if the user might be typing \abc.
+
*map-keys-fails*
There are situations where key codes might not be recognized:
- Vim can only read part of the key code. Mostly this is only the first
diff -r 274c841f033a src/getchar.c
--- a/src/getchar.c Fri Jan 25 20:11:01 2013 +0100
+++ b/src/getchar.c Wed Jan 30 09:13:18 2013 -0500
@@ -1912,6 +1912,7 @@
mapblock_T *mp;
#ifdef FEAT_LOCALMAP
mapblock_T *mp2;
+ int expecting_global_mappings;
#endif
mapblock_T *mp_match;
int mp_match_len = 0;
@@ -2093,6 +2094,7 @@
/* First try buffer-local mappings. */
mp = curbuf->b_maphash[MAP_HASH(local_State, c1)];
mp2 = maphash[MAP_HASH(local_State, c1)];
+ expecting_global_mappings = (mp && mp2);
if (mp == NULL)
{
mp = mp2;
@@ -2116,6 +2118,20 @@
#endif
(mp = mp->m_next))
{
+#ifdef FEAT_LOCALMAP
+ if (expecting_global_mappings && mp2 == NULL)
+ {
+ /*
+ * This is the first global mapping. If we've
+ * got a complete buffer-local match, use it.
+ */
+ if (mp_match)

Michael Henry

unread,
Feb 15, 2013, 5:57:18 PM2/15/13
to vim...@googlegroups.com, Bram Moolenaar
Bram,

Life's been a bit hectic lately (as I'm sure you'd find familiar
:-)), and I haven't had a chance to follow up regarding the
various options for the idea below. The original patch provided
a Vim option to control the behavior of buffer-local mappings
that have global mappings as prefixes. The modified patch below
removed the idea of a new Vim option.

If there is anything you'd like me to adjust about either patch
to make the change suitable for inclusion, please let me know.

Thanks,
Michael Henry

Bram Moolenaar

unread,
Feb 17, 2013, 12:11:21 PM2/17/13
to Michael Henry, vim...@googlegroups.com

Michael -

> Life's been a bit hectic lately (as I'm sure you'd find familiar
> :-)), and I haven't had a chance to follow up regarding the
> various options for the idea below. The original patch provided
> a Vim option to control the behavior of buffer-local mappings
> that have global mappings as prefixes. The modified patch below
> removed the idea of a new Vim option.
>
> If there is anything you'd like me to adjust about either patch
> to make the change suitable for inclusion, please let me know.

Thanks for the patch. Bug fixes go first, thus it's somewhere down in
the todo list.

- Bram

--
./configure
Checking whether build environment is sane ...
build environment is grinning and holding a spatula. Guess not.

Michael Henry

unread,
Feb 17, 2013, 2:02:32 PM2/17/13
to Bram Moolenaar, vim...@googlegroups.com
On 02/17/2013 12:11 PM, Bram Moolenaar wrote:
> On 02/15/2013 05:57 PM, Michael Henry wrote:
>> If there is anything you'd like me to adjust about either patch
>> to make the change suitable for inclusion, please let me know.
>
> Thanks for the patch. Bug fixes go first, thus it's somewhere down in
> the todo list.

That's great - thanks! I didn't think to check the TODO list,
but I see it's there. I'd thought there might something more I
should be doing here, but I certainly agree with fixing bugs
before adding features.

Michael Henry

Reply all
Reply to author
Forward
0 new messages