Bug/non-determinism in output of maparg() and map commands

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
36 messages Options
12
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Bug/non-determinism in output of maparg() and map commands

brettstahlman
I would like to be able to save and restore mappings programmatically. In order to do so, however, I need to be able to determine the exact lhs and rhs used to create the mapping. I had assumed the lhs/rhs members of the dictionary returned by maparg() would provided this information, but as the simple example below illustrates, it does not. The problem is that both the 4 character mapping...
"<F7>"
...and the single character function key mapping produce the same output for maparg()['lhs'] and `nmap <buffer>'. (The nmap output does at least colorize the function key, but the text output is identical.) The 'rhs' member seems to be treated slightly differently, but still inconsistently: in particular, for the maps created under default 'cpo', 'rhs' looks correct, but for <F9> (created with '<' in 'cpo'), it looks wrong. I suspect what's happening is that 'rhs' is always reporting exactly what was typed, but since in the general case I have no way of knowing what 'cpo' was in effect when the map was created, this isn't very helpful.

Is there a programmatic way to determine lhs and rhs in some sort of canonical form? Also helpful would be if hasmapto() (or something like it) told you exactly which lhs was mapped to the specified rhs. (I realize there could be more than one, since hasmapto() doesn't require an exact match, but there's a similar situation with mapcheck(), and it just picks one mapping to return, which can be useful in certain use cases.) More generally, it seems there should be some way to determine a list of lhs from a given rhs...

-- Test commands --
set cpo&
nmap <buffer> <F7> <F7>
nmap <buffer> <lt>F8> <lt>F8>
set cpo+=<
nmap <buffer> <F9> <F9>
set cpo&

-- Output of nmap and maparg --
nmap <buffer>
n  <F9>         @<F9>
n  <F8>         @<F8>
n  <F7>         @<F7>

echo maparg('<F7>', 'n', 0, 1)
{'silent': 0, 'noremap': 0, 'lhs': '<F7>', 'mode': 'n', 'nowait': 0, 'expr': 0, 'sid': 66, 'rhs': '<F7>', 'buffer': 1}
echo maparg('<lt>F8>', 'n', 0, 1)
{'silent': 0, 'noremap': 0, 'lhs': '<F8>', 'mode': 'n', 'nowait': 0, 'expr': 0, 'sid': 66, 'rhs': '<lt>F8>', 'buffer': 1}
echo maparg('<lt>F9>', 'n', 0, 1)
{'silent': 0, 'noremap': 0, 'lhs': '<F9>', 'mode': 'n', 'nowait': 0, 'expr': 0, 'sid': 66, 'rhs': '<F9>', 'buffer': 1}

Thanks,
Brett Stahlman

--
--
You received this message from the "vim_use" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

---
You received this message because you are subscribed to the Google Groups "vim_use" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Bug/non-determinism in output of maparg() and map commands

Bram Moolenaar

Brett Stahlman wrote:

> I would like to be able to save and restore mappings programmatically. In
> order to do so, however, I need to be able to determine the exact lhs and
> rhs used to create the mapping. I had assumed the lhs/rhs members of the
> dictionary returned by maparg() would provided this information, but as the
> simple example below illustrates, it does not. The problem is that both the
> 4 character mapping...
> "<F7>"
> ...and the single character function key mapping produce the same output
> for maparg()['lhs'] and `nmap <buffer>'. (The nmap output does at least
> colorize the function key, but the text output is identical.) The 'rhs'
> member seems to be treated slightly differently, but still inconsistently:
> in particular, for the maps created under default 'cpo', 'rhs' looks
> correct, but for <F9> (created with '<' in 'cpo'), it looks wrong. I
> suspect what's happening is that 'rhs' is always reporting exactly what was
> typed, but since in the general case I have no way of knowing what 'cpo'
> was in effect when the map was created, this isn't very helpful.
>
> Is there a programmatic way to determine lhs and rhs in some sort of
> canonical form? Also helpful would be if hasmapto() (or something like it)
> told you exactly which lhs was mapped to the specified rhs. (I realize
> there could be more than one, since hasmapto() doesn't require an exact
> match, but there's a similar situation with mapcheck(), and it just picks
> one mapping to return, which can be useful in certain use cases.) More
> generally, it seems there should be some way to determine a list of lhs
> from a given rhs...
>
> -- Test commands --
> set cpo&
> nmap <buffer> <F7> <F7>
> nmap <buffer> <lt>F8> <lt>F8>
> set cpo+=<
> nmap <buffer> <F9> <F9>
> set cpo&
>
> -- Output of nmap and maparg --
> nmap <buffer>
> n  <F9>         @<F9>
> n  <F8>         @<F8>
> n  <F7>         @<F7>
>
> echo maparg('<F7>', 'n', 0, 1)
> {'silent': 0, 'noremap': 0, 'lhs': '<F7>', 'mode': 'n', 'nowait': 0,
> 'expr': 0, 'sid': 66, 'rhs': '<F7>', 'buffer': 1}
> echo maparg('<lt>F8>', 'n', 0, 1)
> {'silent': 0, 'noremap': 0, 'lhs': '<F8>', 'mode': 'n', 'nowait': 0,
> 'expr': 0, 'sid': 66, 'rhs': '<lt>F8>', 'buffer': 1}
> echo maparg('<lt>F9>', 'n', 0, 1)
> {'silent': 0, 'noremap': 0, 'lhs': '<F9>', 'mode': 'n', 'nowait': 0,
> 'expr': 0, 'sid': 66, 'rhs': '<F9>', 'buffer': 1}

The best solution is probably to also add the raw rhs, with the terminal
codes replaced.  This won't work when changing the terminal type, but
that is very unlikely to happen.

--
ARTHUR: The swallow may fly south with the sun, or the house martin or the
        plover seek warmer hot lands in winter, yet these are not strangers to
        our land.
SOLDIER: Are you suggesting coconuts migrate?
                 "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD

 /// Bram Moolenaar -- [hidden email] -- 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    ///

--
--
You received this message from the "vim_use" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

---
You received this message because you are subscribed to the Google Groups "vim_use" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Bug/non-determinism in output of maparg() and map commands

brettstahlman
On Tue, May 23, 2017 at 4:35 AM, Bram Moolenaar <[hidden email]> wrote:

>
> Brett Stahlman wrote:
>
>> I would like to be able to save and restore mappings programmatically. In
>> order to do so, however, I need to be able to determine the exact lhs and
>> rhs used to create the mapping. I had assumed the lhs/rhs members of the
>> dictionary returned by maparg() would provided this information, but as the
>> simple example below illustrates, it does not. The problem is that both the
>> 4 character mapping...
>> "<F7>"
>> ...and the single character function key mapping produce the same output
>> for maparg()['lhs'] and `nmap <buffer>'. (The nmap output does at least
>> colorize the function key, but the text output is identical.) The 'rhs'
>> member seems to be treated slightly differently, but still inconsistently:
>> in particular, for the maps created under default 'cpo', 'rhs' looks
>> correct, but for <F9> (created with '<' in 'cpo'), it looks wrong. I
>> suspect what's happening is that 'rhs' is always reporting exactly what was
>> typed, but since in the general case I have no way of knowing what 'cpo'
>> was in effect when the map was created, this isn't very helpful.
>>
>> Is there a programmatic way to determine lhs and rhs in some sort of
>> canonical form? Also helpful would be if hasmapto() (or something like it)
>> told you exactly which lhs was mapped to the specified rhs. (I realize
>> there could be more than one, since hasmapto() doesn't require an exact
>> match, but there's a similar situation with mapcheck(), and it just picks
>> one mapping to return, which can be useful in certain use cases.) More
>> generally, it seems there should be some way to determine a list of lhs
>> from a given rhs...
>>
>> -- Test commands --
>> set cpo&
>> nmap <buffer> <F7> <F7>
>> nmap <buffer> <lt>F8> <lt>F8>
>> set cpo+=<
>> nmap <buffer> <F9> <F9>
>> set cpo&
>>
>> -- Output of nmap and maparg --
>> nmap <buffer>
>> n  <F9>         @<F9>
>> n  <F8>         @<F8>
>> n  <F7>         @<F7>
>>
>> echo maparg('<F7>', 'n', 0, 1)
>> {'silent': 0, 'noremap': 0, 'lhs': '<F7>', 'mode': 'n', 'nowait': 0,
>> 'expr': 0, 'sid': 66, 'rhs': '<F7>', 'buffer': 1}
>> echo maparg('<lt>F8>', 'n', 0, 1)
>> {'silent': 0, 'noremap': 0, 'lhs': '<F8>', 'mode': 'n', 'nowait': 0,
>> 'expr': 0, 'sid': 66, 'rhs': '<lt>F8>', 'buffer': 1}
>> echo maparg('<lt>F9>', 'n', 0, 1)
>> {'silent': 0, 'noremap': 0, 'lhs': '<F9>', 'mode': 'n', 'nowait': 0,
>> 'expr': 0, 'sid': 66, 'rhs': '<F9>', 'buffer': 1}
>
> The best solution is probably to also add the raw rhs, with the terminal
> codes replaced.  This won't work when changing the terminal type, but
> that is very unlikely to happen.

You mean adding a key such as "raw_rhs" to the dictionary returned by
maparg()? If so, then yes this would help, but there would still need to
be a way to determine lhs, which is currently even more ambiguous than
rhs. While it's true that I probably already have lhs if I'm calling
maparg(), I need a way to determine which lhs(s) is/are ambiguous with a
given lhs. Mapcheck() gives me only the rhs of the conflicting map. To
save and restore, I'd need to know the lhs in canonical form as well.

When you say "with the terminal codes replaced", do you mean replaced
using method #2 or #3 under :map-special-keys? These both sound as
though they could provide a canonical form for both lhs and rhs, and
unless I'm misunderstanding the paragraph beginning "The advantage of
the second and third method...", these methods would not be broken by a
terminal switch...

Thanks,
Brett S.

>
> --
> ARTHUR: The swallow may fly south with the sun, or the house martin or the
>         plover seek warmer hot lands in winter, yet these are not strangers to
>         our land.
> SOLDIER: Are you suggesting coconuts migrate?
>                  "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD
>
>  /// Bram Moolenaar -- [hidden email] -- 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    ///

--
--
You received this message from the "vim_use" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

---
You received this message because you are subscribed to the Google Groups "vim_use" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Bug/non-determinism in output of maparg() and map commands

brettstahlman
On Tuesday, May 23, 2017 at 8:25:33 AM UTC-5, Brett Stahlman wrote:
> On Tue, May 23, 2017 at 4:35 AM, Bram Moolenaar <[hidden email]> wrote:
> >
> > Brett Stahlman wrote:
> >
%--snip--%

> >
> > The best solution is probably to also add the raw rhs, with the terminal
> > codes replaced.  This won't work when changing the terminal type, but
> > that is very unlikely to happen.
>
> You mean adding a key such as "raw_rhs" to the dictionary returned by
> maparg()? If so, then yes this would help, but there would still need to
> be a way to determine lhs, which is currently even more ambiguous than
> rhs. While it's true that I probably already have lhs if I'm calling
> maparg(), I need a way to determine which lhs(s) is/are ambiguous with a
> given lhs. Mapcheck() gives me only the rhs of the conflicting map. To
> save and restore, I'd need to know the lhs in canonical form as well.
Perhaps mapcheck() could take an optional arg requesting something more than a simple boolean return. When called with this extra arg, mapcheck() could return a conflicting/ambiguous lhs (or list thereof) in some canonical format (possibly determined by the value of the extra arg itself). As long as the format returned could be fed to maparg(), it would be possible to find conflicting mappings, remove them temporarily, and subsequently restore them...

Thanks,
Brett S.

%--snip--%


--
--
You received this message from the "vim_use" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

---
You received this message because you are subscribed to the Google Groups "vim_use" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Bug/non-determinism in output of maparg() and map commands

Bram Moolenaar

Brett Stahlman wrote:

> On Tuesday, May 23, 2017 at 8:25:33 AM UTC-5, Brett Stahlman wrote:
> > On Tue, May 23, 2017 at 4:35 AM, Bram Moolenaar <[hidden email]> wrote:
> > >
> > > Brett Stahlman wrote:
> > >
> %--snip--%
> > >
> > > The best solution is probably to also add the raw rhs, with the terminal
> > > codes replaced.  This won't work when changing the terminal type, but
> > > that is very unlikely to happen.
> >
> > You mean adding a key such as "raw_rhs" to the dictionary returned by
> > maparg()? If so, then yes this would help, but there would still need to
> > be a way to determine lhs, which is currently even more ambiguous than
> > rhs. While it's true that I probably already have lhs if I'm calling
> > maparg(), I need a way to determine which lhs(s) is/are ambiguous with a
> > given lhs. Mapcheck() gives me only the rhs of the conflicting map. To
> > save and restore, I'd need to know the lhs in canonical form as well.
>
> Perhaps mapcheck() could take an optional arg requesting something more than a simple boolean return. When called with this extra arg, mapcheck() could return a conflicting/ambiguous lhs (or list thereof) in some canonical format (possibly determined by the value of the extra arg itself). As long as the format returned could be fed to maparg(), it would be possible to find conflicting mappings, remove them temporarily, and subsequently restore them...

If you define a mapping you will want to know whether the mapping
already exists and needs to be restored.  For that you can use maparg(),
no need to use mapcheck().

Not sure why you would want to remove "conflicting" mappings. Perhaps
when you map the ; key, and the user has ;x mapped?  Then you would need
a list.  Adding a maplist() function would be better than adding
arguments to mapcheck().


--
Kiss me twice.  I'm schizophrenic.

 /// Bram Moolenaar -- [hidden email] -- 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    ///

--
--
You received this message from the "vim_use" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

---
You received this message because you are subscribed to the Google Groups "vim_use" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Bug/non-determinism in output of maparg() and map commands

brettstahlman
On Wed, May 24, 2017 at 2:08 AM, Bram Moolenaar <[hidden email]> wrote:

>
> Brett Stahlman wrote:
>
>> On Tuesday, May 23, 2017 at 8:25:33 AM UTC-5, Brett Stahlman wrote:
>> > On Tue, May 23, 2017 at 4:35 AM, Bram Moolenaar <[hidden email]> wrote:
>> > >
>> > > Brett Stahlman wrote:
>> > >
>> %--snip--%
>> > >
>> > > The best solution is probably to also add the raw rhs, with the terminal
>> > > codes replaced.  This won't work when changing the terminal type, but
>> > > that is very unlikely to happen.
>> >
>> > You mean adding a key such as "raw_rhs" to the dictionary returned by
>> > maparg()? If so, then yes this would help, but there would still need to
>> > be a way to determine lhs, which is currently even more ambiguous than
>> > rhs. While it's true that I probably already have lhs if I'm calling
>> > maparg(), I need a way to determine which lhs(s) is/are ambiguous with a
>> > given lhs. Mapcheck() gives me only the rhs of the conflicting map. To
>> > save and restore, I'd need to know the lhs in canonical form as well.
>>
>> Perhaps mapcheck() could take an optional arg requesting something more than a simple boolean return. When called with this extra arg, mapcheck() could return a conflicting/ambiguous lhs (or list thereof) in some canonical format (possibly determined by the value of the extra arg itself). As long as the format returned could be fed to maparg(), it would be possible to find conflicting mappings, remove them temporarily, and subsequently restore them...
>
> If you define a mapping you will want to know whether the mapping
> already exists and needs to be restored.  For that you can use maparg(),
> no need to use mapcheck().
>
> Not sure why you would want to remove "conflicting" mappings. Perhaps
> when you map the ; key, and the user has ;x mapped?  Then you would need
> a list.  Adding a maplist() function would be better than adding
> arguments to mapcheck().

Yes. Very much like that. I'm implementing a sort of transient mode, in
which I'll "shadow" existing maps with very short (generally single
character) mappings, which are expected to be ambiguous/conflicting with
existing maps, and even builtin operators. Of course, when I exit the
transient mode, I'd need to restore the mappings that were shadowed.

The global and builtin maps are not a problem, since the transient maps use
<buffer> and <nowait>; however, without parsing the output of one of the :map
functions, I have no way of knowing which buf-local mappings will be ambiguous
with the transient maps I'm defining. And parsing the :map output is
problematic for the reasons already mentioned: e.g., no way to tell the
difference between function key <F8> and the corresponding 4 characters. I'd
actually considered taking some sort of iterative approach: e.g., trying all
possible permutations of lhs as input to maparg() and testing the results, in
an attempt to deduce the canonical form, but this would be extremely messy,
and I don't even know whether it would be deterministic... The maplist()
function you mentioned, if it returned all ambiguous left hand sides in
canonical form, or even a list of the corresponding maparg()-style
dictionaries, would be perfect. Of course, there would also need to be a way
to get the rhs's canonical form: e.g., the extra "raw_rhs" key in the maparg()
or maplist() dictionary.

Thanks,
Brett S.

>
>
> --
> Kiss me twice.  I'm schizophrenic.
>
>  /// Bram Moolenaar -- [hidden email] -- 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    ///

--
--
You received this message from the "vim_use" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

---
You received this message because you are subscribed to the Google Groups "vim_use" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Bug/non-determinism in output of maparg() and map commands

brettstahlman
On Wed, May 24, 2017 at 9:28 AM, Brett Stahlman <[hidden email]> wrote:

> On Wed, May 24, 2017 at 2:08 AM, Bram Moolenaar <[hidden email]> wrote:
>>
>> Brett Stahlman wrote:
>>
>>> On Tuesday, May 23, 2017 at 8:25:33 AM UTC-5, Brett Stahlman wrote:
>>> > On Tue, May 23, 2017 at 4:35 AM, Bram Moolenaar <[hidden email]> wrote:
>>> > >
>>> > > Brett Stahlman wrote:
>>> > >
>>> %--snip--%
>>> > >
>>> > > The best solution is probably to also add the raw rhs, with the terminal
>>> > > codes replaced.  This won't work when changing the terminal type, but
>>> > > that is very unlikely to happen.
>>> >
>>> > You mean adding a key such as "raw_rhs" to the dictionary returned by
>>> > maparg()? If so, then yes this would help, but there would still need to
>>> > be a way to determine lhs, which is currently even more ambiguous than
>>> > rhs. While it's true that I probably already have lhs if I'm calling
>>> > maparg(), I need a way to determine which lhs(s) is/are ambiguous with a
>>> > given lhs. Mapcheck() gives me only the rhs of the conflicting map. To
>>> > save and restore, I'd need to know the lhs in canonical form as well.
>>>
>>> Perhaps mapcheck() could take an optional arg requesting something more than a simple boolean return. When called with this extra arg, mapcheck() could return a conflicting/ambiguous lhs (or list thereof) in some canonical format (possibly determined by the value of the extra arg itself). As long as the format returned could be fed to maparg(), it would be possible to find conflicting mappings, remove them temporarily, and subsequently restore them...
>>
>> If you define a mapping you will want to know whether the mapping
>> already exists and needs to be restored.  For that you can use maparg(),
>> no need to use mapcheck().
>>
>> Not sure why you would want to remove "conflicting" mappings. Perhaps
>> when you map the ; key, and the user has ;x mapped?  Then you would need
>> a list.  Adding a maplist() function would be better than adding
>> arguments to mapcheck().
>
> Yes. Very much like that. I'm implementing a sort of transient mode, in
> which I'll "shadow" existing maps with very short (generally single
> character) mappings, which are expected to be ambiguous/conflicting with
> existing maps, and even builtin operators. Of course, when I exit the
> transient mode, I'd need to restore the mappings that were shadowed.
>
> The global and builtin maps are not a problem, since the transient maps use
> <buffer> and <nowait>; however, without parsing the output of one of the :map
> functions, I have no way of knowing which buf-local mappings will be ambiguous
> with the transient maps I'm defining. And parsing the :map output is
> problematic for the reasons already mentioned: e.g., no way to tell the
> difference between function key <F8> and the corresponding 4 characters. I'd
> actually considered taking some sort of iterative approach: e.g., trying all
> possible permutations of lhs as input to maparg() and testing the results, in
> an attempt to deduce the canonical form, but this would be extremely messy,
> and I don't even know whether it would be deterministic... The maplist()
> function you mentioned, if it returned all ambiguous left hand sides in
> canonical form, or even a list of the corresponding maparg()-style
> dictionaries, would be perfect. Of course, there would also need to be a way
> to get the rhs's canonical form: e.g., the extra "raw_rhs" key in the maparg()
> or maplist() dictionary.

One other possibility occurs to me... I haven't looked at the internal
map representation, but I'm guessing it might be possible to return
some sort of "map handle" for ambiguous maps, rather than lhs/rhs in
canonical form. These handles could be passed to functions like
disable_map() and enable_map(), and would preserve the opacity of the
implementation. Just a thought...

Thanks,
Brett S.

>
> Thanks,
> Brett S.
>
>>
>>
>> --
>> Kiss me twice.  I'm schizophrenic.
>>
>>  /// Bram Moolenaar -- [hidden email] -- 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    ///

--
--
You received this message from the "vim_use" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

---
You received this message because you are subscribed to the Google Groups "vim_use" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Bug/non-determinism in output of maparg() and map commands

Bram Moolenaar
In reply to this post by brettstahlman

Brett Stahlman wrote:

> >> On Tuesday, May 23, 2017 at 8:25:33 AM UTC-5, Brett Stahlman wrote:
> >> > On Tue, May 23, 2017 at 4:35 AM, Bram Moolenaar <[hidden email]> wrote:
> >> > >
> >> > > Brett Stahlman wrote:
> >> > >
> >> %--snip--%
> >> > >
> >> > > The best solution is probably to also add the raw rhs, with the terminal
> >> > > codes replaced.  This won't work when changing the terminal type, but
> >> > > that is very unlikely to happen.
> >> >
> >> > You mean adding a key such as "raw_rhs" to the dictionary returned by
> >> > maparg()? If so, then yes this would help, but there would still need to
> >> > be a way to determine lhs, which is currently even more ambiguous than
> >> > rhs. While it's true that I probably already have lhs if I'm calling
> >> > maparg(), I need a way to determine which lhs(s) is/are ambiguous with a
> >> > given lhs. Mapcheck() gives me only the rhs of the conflicting map. To
> >> > save and restore, I'd need to know the lhs in canonical form as well.
> >>
> >> Perhaps mapcheck() could take an optional arg requesting something more than a simple boolean return. When called with this extra arg, mapcheck() could return a conflicting/ambiguous lhs (or list thereof) in some canonical format (possibly determined by the value of the extra arg itself). As long as the format returned could be fed to maparg(), it would be possible to find conflicting mappings, remove them temporarily, and subsequently restore them...
> >
> > If you define a mapping you will want to know whether the mapping
> > already exists and needs to be restored.  For that you can use maparg(),
> > no need to use mapcheck().
> >
> > Not sure why you would want to remove "conflicting" mappings. Perhaps
> > when you map the ; key, and the user has ;x mapped?  Then you would need
> > a list.  Adding a maplist() function would be better than adding
> > arguments to mapcheck().
>
> Yes. Very much like that. I'm implementing a sort of transient mode, in
> which I'll "shadow" existing maps with very short (generally single
> character) mappings, which are expected to be ambiguous/conflicting with
> existing maps, and even builtin operators. Of course, when I exit the
> transient mode, I'd need to restore the mappings that were shadowed.
>
> The global and builtin maps are not a problem, since the transient maps use
> <buffer> and <nowait>; however, without parsing the output of one of the :map
> functions, I have no way of knowing which buf-local mappings will be ambiguous
> with the transient maps I'm defining. And parsing the :map output is
> problematic for the reasons already mentioned: e.g., no way to tell the
> difference between function key <F8> and the corresponding 4 characters. I'd
> actually considered taking some sort of iterative approach: e.g., trying all
> possible permutations of lhs as input to maparg() and testing the results, in
> an attempt to deduce the canonical form, but this would be extremely messy,
> and I don't even know whether it would be deterministic... The maplist()
> function you mentioned, if it returned all ambiguous left hand sides in
> canonical form, or even a list of the corresponding maparg()-style
> dictionaries, would be perfect. Of course, there would also need to be a way
> to get the rhs's canonical form: e.g., the extra "raw_rhs" key in the maparg()
> or maplist() dictionary.

OK, so for this you would use maplist() to get the list of mappings to
disable, use maparg() to get the current mapping, clear the mapping, do
your stuff, then restore the cleared mappings.  You then need to make
sure you restore the mappings exactly as they were, even when your
"stuff" fails miserably.

It's a lot easier if we would have a way to temporarily disable
mappings.  It's mostly the same as above, but you won't need to use
maparg() to get the current mapping and the restore operation.  Instead
you would disable instead of clear, and later re-enable instead of
restore.  Still need to make sure the re-enbling does happen, no change
in that part.

Big advantage is that if we evern add functionality to mappings, it will
keep working, while your save/restore pair probably fails.

Ah, your later post goes in the same direction.

--
DENNIS: Look,  strange women lying on their backs in ponds handing out
        swords ... that's no basis for a system of government.  Supreme
        executive power derives from a mandate from the masses, not from some
        farcical aquatic ceremony.
                 "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD

 /// Bram Moolenaar -- [hidden email] -- 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    ///

--
--
You received this message from the "vim_use" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

---
You received this message because you are subscribed to the Google Groups "vim_use" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Bug/non-determinism in output of maparg() and map commands

brettstahlman
On Fri, May 26, 2017 at 12:43 PM, Bram Moolenaar <[hidden email]> wrote:
>
> Brett Stahlman wrote:
>

%--snip--%

>>
>> Yes. Very much like that. I'm implementing a sort of transient mode, in
>> which I'll "shadow" existing maps with very short (generally single
>> character) mappings, which are expected to be ambiguous/conflicting with
>> existing maps, and even builtin operators. Of course, when I exit the
>> transient mode, I'd need to restore the mappings that were shadowed.
>>
>> The global and builtin maps are not a problem, since the transient maps use
>> <buffer> and <nowait>; however, without parsing the output of one of the :map
>> functions, I have no way of knowing which buf-local mappings will be ambiguous
>> with the transient maps I'm defining. And parsing the :map output is
>> problematic for the reasons already mentioned: e.g., no way to tell the
>> difference between function key <F8> and the corresponding 4 characters. I'd
>> actually considered taking some sort of iterative approach: e.g., trying all
>> possible permutations of lhs as input to maparg() and testing the results, in
>> an attempt to deduce the canonical form, but this would be extremely messy,
>> and I don't even know whether it would be deterministic... The maplist()
>> function you mentioned, if it returned all ambiguous left hand sides in
>> canonical form, or even a list of the corresponding maparg()-style
>> dictionaries, would be perfect. Of course, there would also need to be a way
>> to get the rhs's canonical form: e.g., the extra "raw_rhs" key in the maparg()
>> or maplist() dictionary.
>
> OK, so for this you would use maplist() to get the list of mappings to
> disable, use maparg() to get the current mapping, clear the mapping, do
> your stuff, then restore the cleared mappings.  You then need to make
> sure you restore the mappings exactly as they were, even when your
> "stuff" fails miserably.
>
> It's a lot easier if we would have a way to temporarily disable
> mappings.  It's mostly the same as above, but you won't need to use
> maparg() to get the current mapping and the restore operation.  Instead
> you would disable instead of clear, and later re-enable instead of
> restore.  Still need to make sure the re-enbling does happen, no change
> in that part.
>
> Big advantage is that if we evern add functionality to mappings, it will
> keep working, while your save/restore pair probably fails.
>
> Ah, your later post goes in the same direction.

Yes. My thinking exactly. No need to translate back and forth between
internal and external representations if it's not too difficult to
provide a clean interface supporting sensible map operations at a
higher level.

So then maplist() would return a list of handles of some sort (rather
than a list of lhs)? I suppose the interface could even support
functions for mapping a handle to the information currently returned
in the maparg() dictionary (but perhaps with lhs/rhs in canonical
form) - unless you feel that would be leaking too much internal
state... I guess the bottom line is that if there is still a valid use
case for remapping/executing the rhs returned by maparg(), there
should probably be a way to obtain it in a more deterministic format.
E.g., I'm thinking this example (from the help on maparg()) might be
susceptible to breakage if user changes 'cpo' after mappings have been
created...

exe 'nnoremap <Tab> ==' . maparg('<Tab>', 'n')

To make this sort of operation safer, the map interface could provide
an "execute" function, which would allow you to execute even a
currently disabled or shadowed map on demand. Something along the
lines of what might be achieved with maparg(), :normal!, or even
feedkeys(), but cleaner and safer. At any rate, the enable/disable
functions would suffice for the save/restore use case I described...

Thanks,
Brett S.

>
> --
> DENNIS: Look,  strange women lying on their backs in ponds handing out
>         swords ... that's no basis for a system of government.  Supreme
>         executive power derives from a mandate from the masses, not from some
>         farcical aquatic ceremony.
>                  "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD
>
>  /// Bram Moolenaar -- [hidden email] -- 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    ///

--
--
You received this message from the "vim_use" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

---
You received this message because you are subscribed to the Google Groups "vim_use" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Bug/non-determinism in output of maparg() and map commands

Nikolay Aleksandrovich Pavlov
In reply to this post by Bram Moolenaar
2017-05-26 20:43 GMT+03:00 Bram Moolenaar <[hidden email]>:

>
> Brett Stahlman wrote:
>
>> >> On Tuesday, May 23, 2017 at 8:25:33 AM UTC-5, Brett Stahlman wrote:
>> >> > On Tue, May 23, 2017 at 4:35 AM, Bram Moolenaar <[hidden email]> wrote:
>> >> > >
>> >> > > Brett Stahlman wrote:
>> >> > >
>> >> %--snip--%
>> >> > >
>> >> > > The best solution is probably to also add the raw rhs, with the terminal
>> >> > > codes replaced.  This won't work when changing the terminal type, but
>> >> > > that is very unlikely to happen.
>> >> >
>> >> > You mean adding a key such as "raw_rhs" to the dictionary returned by
>> >> > maparg()? If so, then yes this would help, but there would still need to
>> >> > be a way to determine lhs, which is currently even more ambiguous than
>> >> > rhs. While it's true that I probably already have lhs if I'm calling
>> >> > maparg(), I need a way to determine which lhs(s) is/are ambiguous with a
>> >> > given lhs. Mapcheck() gives me only the rhs of the conflicting map. To
>> >> > save and restore, I'd need to know the lhs in canonical form as well.
>> >>
>> >> Perhaps mapcheck() could take an optional arg requesting something more than a simple boolean return. When called with this extra arg, mapcheck() could return a conflicting/ambiguous lhs (or list thereof) in some canonical format (possibly determined by the value of the extra arg itself). As long as the format returned could be fed to maparg(), it would be possible to find conflicting mappings, remove them temporarily, and subsequently restore them...
>> >
>> > If you define a mapping you will want to know whether the mapping
>> > already exists and needs to be restored.  For that you can use maparg(),
>> > no need to use mapcheck().
>> >
>> > Not sure why you would want to remove "conflicting" mappings. Perhaps
>> > when you map the ; key, and the user has ;x mapped?  Then you would need
>> > a list.  Adding a maplist() function would be better than adding
>> > arguments to mapcheck().
>>
>> Yes. Very much like that. I'm implementing a sort of transient mode, in
>> which I'll "shadow" existing maps with very short (generally single
>> character) mappings, which are expected to be ambiguous/conflicting with
>> existing maps, and even builtin operators. Of course, when I exit the
>> transient mode, I'd need to restore the mappings that were shadowed.
>>
>> The global and builtin maps are not a problem, since the transient maps use
>> <buffer> and <nowait>; however, without parsing the output of one of the :map
>> functions, I have no way of knowing which buf-local mappings will be ambiguous
>> with the transient maps I'm defining. And parsing the :map output is
>> problematic for the reasons already mentioned: e.g., no way to tell the
>> difference between function key <F8> and the corresponding 4 characters. I'd
>> actually considered taking some sort of iterative approach: e.g., trying all
>> possible permutations of lhs as input to maparg() and testing the results, in
>> an attempt to deduce the canonical form, but this would be extremely messy,
>> and I don't even know whether it would be deterministic... The maplist()
>> function you mentioned, if it returned all ambiguous left hand sides in
>> canonical form, or even a list of the corresponding maparg()-style
>> dictionaries, would be perfect. Of course, there would also need to be a way
>> to get the rhs's canonical form: e.g., the extra "raw_rhs" key in the maparg()
>> or maplist() dictionary.
>
> OK, so for this you would use maplist() to get the list of mappings to
> disable, use maparg() to get the current mapping, clear the mapping, do
> your stuff, then restore the cleared mappings.  You then need to make
> sure you restore the mappings exactly as they were, even when your
> "stuff" fails miserably.
>
> It's a lot easier if we would have a way to temporarily disable
> mappings.  It's mostly the same as above, but you won't need to use
> maparg() to get the current mapping and the restore operation.  Instead
> you would disable instead of clear, and later re-enable instead of
> restore.  Still need to make sure the re-enbling does happen, no change
> in that part.

Not sure I understood what exactly you suggest to disable/restore. All
mappings at once with one command? I would actually disagree here: I
need something similar for translit3, but it only remaps
single-character mappings, leaving most of other user mappings alone.
One mapping at a time? It would be good, but given that request is
temporary remapping naming the functionality enable/disable looks
strange. And there are still issues with determining {lhs}.

One of the logical variants would be `:map <push> {lhs}
{new-rhs}`/`:unmap <push> {lhs}`+`:map <pop> {lhs}`, but this is hard
to implement and is rather limited, though less limited then
enable/disable everything variant.

I would instead suggest a function mappings_dump()/mappings_add():
first is similar to `nvim[_buf]_get_keymap` and should dump all
mappings as a list of maparg()-like dictionaries. Second should define
mappings being given a list of them. Of course, this means that
dictionaries need to be fixed to allow correctly saving/restoring.

The advantages:

1. Easier to implement. Code for creating a maparg() dictionary is
already there, iterating over all mappings is not a problem. Results
needs to be incompatible with maparg() or use additional keys though:
e.g. Neovim altered the contents of `noremap` and `buffer` keys: first
is now 0, 1 or 2 (you can’t correctly restore a mapping if you can’t
distinguish `map <script>` and `noremap`) and second is a buffer
number or zero.
2. More flexible: you can save and restore everything, push or pop
individual mappings, create a temporary mapping which is just like
mapping X, but has `<Plug>(Translit3TemporaryMap)` lhs instead (to be
returned from `<expr>` mappings in order to select either plugin
behaviour or fall back to previously present user mapping instead).

   I can imagine other usages enable/disable or push/pop could not
achieve: generating configuration with mappings like :mkvimrc, but
allows doing adjustments (parsing `:mkvimrc` output is not fun,
especially if you want to be forward compatible), creating a plugin
which analyses how often different mappings are used (need to copy all
mappings to temporary then replace existing mappings with plugin
ones).
3. This is also forward compatible: just need to state in the
documentation that new significant keys may be added in the future to
the dictionaries so they should be preserved.

>
> Big advantage is that if we evern add functionality to mappings, it will
> keep working, while your save/restore pair probably fails.
>
> Ah, your later post goes in the same direction.
>
> --
> DENNIS: Look,  strange women lying on their backs in ponds handing out
>         swords ... that's no basis for a system of government.  Supreme
>         executive power derives from a mandate from the masses, not from some
>         farcical aquatic ceremony.
>                  "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD
>
>  /// Bram Moolenaar -- [hidden email] -- 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    ///
>
> --
> --
> You received this message from the "vim_use" maillist.
> Do not top-post! Type your reply below the text you are replying to.
> For more information, visit http://www.vim.org/maillist.php
>
> ---
> You received this message because you are subscribed to the Google Groups "vim_use" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
> For more options, visit https://groups.google.com/d/optout.

--
--
You received this message from the "vim_use" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

---
You received this message because you are subscribed to the Google Groups "vim_use" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Bug/non-determinism in output of maparg() and map commands

Nikolay Aleksandrovich Pavlov
2017-05-27 0:12 GMT+03:00 Nikolay Aleksandrovich Pavlov <[hidden email]>:

> 2017-05-26 20:43 GMT+03:00 Bram Moolenaar <[hidden email]>:
>>
>> Brett Stahlman wrote:
>>
>>> >> On Tuesday, May 23, 2017 at 8:25:33 AM UTC-5, Brett Stahlman wrote:
>>> >> > On Tue, May 23, 2017 at 4:35 AM, Bram Moolenaar <[hidden email]> wrote:
>>> >> > >
>>> >> > > Brett Stahlman wrote:
>>> >> > >
>>> >> %--snip--%
>>> >> > >
>>> >> > > The best solution is probably to also add the raw rhs, with the terminal
>>> >> > > codes replaced.  This won't work when changing the terminal type, but
>>> >> > > that is very unlikely to happen.
>>> >> >
>>> >> > You mean adding a key such as "raw_rhs" to the dictionary returned by
>>> >> > maparg()? If so, then yes this would help, but there would still need to
>>> >> > be a way to determine lhs, which is currently even more ambiguous than
>>> >> > rhs. While it's true that I probably already have lhs if I'm calling
>>> >> > maparg(), I need a way to determine which lhs(s) is/are ambiguous with a
>>> >> > given lhs. Mapcheck() gives me only the rhs of the conflicting map. To
>>> >> > save and restore, I'd need to know the lhs in canonical form as well.
>>> >>
>>> >> Perhaps mapcheck() could take an optional arg requesting something more than a simple boolean return. When called with this extra arg, mapcheck() could return a conflicting/ambiguous lhs (or list thereof) in some canonical format (possibly determined by the value of the extra arg itself). As long as the format returned could be fed to maparg(), it would be possible to find conflicting mappings, remove them temporarily, and subsequently restore them...
>>> >
>>> > If you define a mapping you will want to know whether the mapping
>>> > already exists and needs to be restored.  For that you can use maparg(),
>>> > no need to use mapcheck().
>>> >
>>> > Not sure why you would want to remove "conflicting" mappings. Perhaps
>>> > when you map the ; key, and the user has ;x mapped?  Then you would need
>>> > a list.  Adding a maplist() function would be better than adding
>>> > arguments to mapcheck().
>>>
>>> Yes. Very much like that. I'm implementing a sort of transient mode, in
>>> which I'll "shadow" existing maps with very short (generally single
>>> character) mappings, which are expected to be ambiguous/conflicting with
>>> existing maps, and even builtin operators. Of course, when I exit the
>>> transient mode, I'd need to restore the mappings that were shadowed.
>>>
>>> The global and builtin maps are not a problem, since the transient maps use
>>> <buffer> and <nowait>; however, without parsing the output of one of the :map
>>> functions, I have no way of knowing which buf-local mappings will be ambiguous
>>> with the transient maps I'm defining. And parsing the :map output is
>>> problematic for the reasons already mentioned: e.g., no way to tell the
>>> difference between function key <F8> and the corresponding 4 characters. I'd
>>> actually considered taking some sort of iterative approach: e.g., trying all
>>> possible permutations of lhs as input to maparg() and testing the results, in
>>> an attempt to deduce the canonical form, but this would be extremely messy,
>>> and I don't even know whether it would be deterministic... The maplist()
>>> function you mentioned, if it returned all ambiguous left hand sides in
>>> canonical form, or even a list of the corresponding maparg()-style
>>> dictionaries, would be perfect. Of course, there would also need to be a way
>>> to get the rhs's canonical form: e.g., the extra "raw_rhs" key in the maparg()
>>> or maplist() dictionary.
>>
>> OK, so for this you would use maplist() to get the list of mappings to
>> disable, use maparg() to get the current mapping, clear the mapping, do
>> your stuff, then restore the cleared mappings.  You then need to make
>> sure you restore the mappings exactly as they were, even when your
>> "stuff" fails miserably.
>>
>> It's a lot easier if we would have a way to temporarily disable
>> mappings.  It's mostly the same as above, but you won't need to use
>> maparg() to get the current mapping and the restore operation.  Instead
>> you would disable instead of clear, and later re-enable instead of
>> restore.  Still need to make sure the re-enbling does happen, no change
>> in that part.
>
> Not sure I understood what exactly you suggest to disable/restore. All
> mappings at once with one command? I would actually disagree here: I
> need something similar for translit3, but it only remaps
> single-character mappings, leaving most of other user mappings alone.
> One mapping at a time? It would be good, but given that request is
> temporary remapping naming the functionality enable/disable looks
> strange. And there are still issues with determining {lhs}.
>
> One of the logical variants would be `:map <push> {lhs}
> {new-rhs}`/`:unmap <push> {lhs}`+`:map <pop> {lhs}`, but this is hard
> to implement and is rather limited, though less limited then
> enable/disable everything variant.
>
> I would instead suggest a function mappings_dump()/mappings_add():
> first is similar to `nvim[_buf]_get_keymap` and should dump all
> mappings as a list of maparg()-like dictionaries. Second should define
> mappings being given a list of them. Of course, this means that
> dictionaries need to be fixed to allow correctly saving/restoring.
>
> The advantages:
>
> 1. Easier to implement. Code for creating a maparg() dictionary is
> already there, iterating over all mappings is not a problem. Results
> needs to be incompatible with maparg() or use additional keys though:
> e.g. Neovim altered the contents of `noremap` and `buffer` keys: first
> is now 0, 1 or 2 (you can’t correctly restore a mapping if you can’t
> distinguish `map <script>` and `noremap`) and second is a buffer
> number or zero.
> 2. More flexible: you can save and restore everything, push or pop
> individual mappings, create a temporary mapping which is just like
> mapping X, but has `<Plug>(Translit3TemporaryMap)` lhs instead (to be
> returned from `<expr>` mappings in order to select either plugin
> behaviour or fall back to previously present user mapping instead).
>
>    I can imagine other usages enable/disable or push/pop could not
> achieve: generating configuration with mappings like :mkvimrc, but
> allows doing adjustments (parsing `:mkvimrc` output is not fun,
> especially if you want to be forward compatible), creating a plugin
> which analyses how often different mappings are used (need to copy all
> mappings to temporary then replace existing mappings with plugin
> ones).

BTW, when we come at it: is not there *already* a code which gets lhs
and rhs in determenistic format for `:mkvimrc`? It should be possible
to just reuse it.

> 3. This is also forward compatible: just need to state in the
> documentation that new significant keys may be added in the future to
> the dictionaries so they should be preserved.
>
>>
>> Big advantage is that if we evern add functionality to mappings, it will
>> keep working, while your save/restore pair probably fails.
>>
>> Ah, your later post goes in the same direction.
>>
>> --
>> DENNIS: Look,  strange women lying on their backs in ponds handing out
>>         swords ... that's no basis for a system of government.  Supreme
>>         executive power derives from a mandate from the masses, not from some
>>         farcical aquatic ceremony.
>>                  "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD
>>
>>  /// Bram Moolenaar -- [hidden email] -- 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    ///
>>
>> --
>> --
>> You received this message from the "vim_use" maillist.
>> Do not top-post! Type your reply below the text you are replying to.
>> For more information, visit http://www.vim.org/maillist.php
>>
>> ---
>> You received this message because you are subscribed to the Google Groups "vim_use" group.
>> To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
>> For more options, visit https://groups.google.com/d/optout.

--
--
You received this message from the "vim_use" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

---
You received this message because you are subscribed to the Google Groups "vim_use" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Bug/non-determinism in output of maparg() and map commands

brettstahlman
In reply to this post by Nikolay Aleksandrovich Pavlov
On Fri, May 26, 2017 at 4:12 PM, Nikolay Aleksandrovich Pavlov
<[hidden email]> wrote:

> 2017-05-26 20:43 GMT+03:00 Bram Moolenaar <[hidden email]>:
>>
>> Brett Stahlman wrote:
>>
>>> >> On Tuesday, May 23, 2017 at 8:25:33 AM UTC-5, Brett Stahlman wrote:
>>> >> > On Tue, May 23, 2017 at 4:35 AM, Bram Moolenaar <[hidden email]> wrote:
>>> >> > >
>>> >> > > Brett Stahlman wrote:
>>> >> > >
>>> >> %--snip--%
>>> >> > >
>>> >> > > The best solution is probably to also add the raw rhs, with the terminal
>>> >> > > codes replaced.  This won't work when changing the terminal type, but
>>> >> > > that is very unlikely to happen.
>>> >> >
>>> >> > You mean adding a key such as "raw_rhs" to the dictionary returned by
>>> >> > maparg()? If so, then yes this would help, but there would still need to
>>> >> > be a way to determine lhs, which is currently even more ambiguous than
>>> >> > rhs. While it's true that I probably already have lhs if I'm calling
>>> >> > maparg(), I need a way to determine which lhs(s) is/are ambiguous with a
>>> >> > given lhs. Mapcheck() gives me only the rhs of the conflicting map. To
>>> >> > save and restore, I'd need to know the lhs in canonical form as well.
>>> >>
>>> >> Perhaps mapcheck() could take an optional arg requesting something more than a simple boolean return. When called with this extra arg, mapcheck() could return a conflicting/ambiguous lhs (or list thereof) in some canonical format (possibly determined by the value of the extra arg itself). As long as the format returned could be fed to maparg(), it would be possible to find conflicting mappings, remove them temporarily, and subsequently restore them...
>>> >
>>> > If you define a mapping you will want to know whether the mapping
>>> > already exists and needs to be restored.  For that you can use maparg(),
>>> > no need to use mapcheck().
>>> >
>>> > Not sure why you would want to remove "conflicting" mappings. Perhaps
>>> > when you map the ; key, and the user has ;x mapped?  Then you would need
>>> > a list.  Adding a maplist() function would be better than adding
>>> > arguments to mapcheck().
>>>
>>> Yes. Very much like that. I'm implementing a sort of transient mode, in
>>> which I'll "shadow" existing maps with very short (generally single
>>> character) mappings, which are expected to be ambiguous/conflicting with
>>> existing maps, and even builtin operators. Of course, when I exit the
>>> transient mode, I'd need to restore the mappings that were shadowed.
>>>
>>> The global and builtin maps are not a problem, since the transient maps use
>>> <buffer> and <nowait>; however, without parsing the output of one of the :map
>>> functions, I have no way of knowing which buf-local mappings will be ambiguous
>>> with the transient maps I'm defining. And parsing the :map output is
>>> problematic for the reasons already mentioned: e.g., no way to tell the
>>> difference between function key <F8> and the corresponding 4 characters. I'd
>>> actually considered taking some sort of iterative approach: e.g., trying all
>>> possible permutations of lhs as input to maparg() and testing the results, in
>>> an attempt to deduce the canonical form, but this would be extremely messy,
>>> and I don't even know whether it would be deterministic... The maplist()
>>> function you mentioned, if it returned all ambiguous left hand sides in
>>> canonical form, or even a list of the corresponding maparg()-style
>>> dictionaries, would be perfect. Of course, there would also need to be a way
>>> to get the rhs's canonical form: e.g., the extra "raw_rhs" key in the maparg()
>>> or maplist() dictionary.
>>
>> OK, so for this you would use maplist() to get the list of mappings to
>> disable, use maparg() to get the current mapping, clear the mapping, do
>> your stuff, then restore the cleared mappings.  You then need to make
>> sure you restore the mappings exactly as they were, even when your
>> "stuff" fails miserably.
>>
>> It's a lot easier if we would have a way to temporarily disable
>> mappings.  It's mostly the same as above, but you won't need to use
>> maparg() to get the current mapping and the restore operation.  Instead
>> you would disable instead of clear, and later re-enable instead of
>> restore.  Still need to make sure the re-enbling does happen, no change
>> in that part.
>
> Not sure I understood what exactly you suggest to disable/restore. All
> mappings at once with one command? I would actually disagree here: I
> need something similar for translit3, but it only remaps
> single-character mappings, leaving most of other user mappings alone.
> One mapping at a time? It would be good, but given that request is
> temporary remapping naming the functionality enable/disable looks
> strange. And there are still issues with determining {lhs}.

No. Not all maps at once. As I understood it, the maplist()
function would return a list of map "handles", each
corresponding to a specific map. It would probably have
optional args that let you request handles for all maps, or
all maps ambiguous/conflicting with a specific lhs, or even
all maps satisfying certain criteria: e.g., buf-local or
global. The enable/disable functions (and any other such
interface functions) would accept a map handle and would
operate only on that map. In addition to the enable/disable
functions, I proposed adding some sort of get_map_info()
function, which would return the information currently
returned in the maparg() dict. Also useful would be an
"execute" function, which would allow you to invoke a map
programmatically through its handle, even if the map is
currently disabled or shadowed (e.g., by a buf-local map).

While this approach could be made completely generic, I can
also see that the <push>/<pop> mechanism could simplify
certain use cases... Of course, just making the lhs/rhs values
deterministic (e.g., by using the code used for mkvimrc) would
definitely be an improvement, especially if some sort of
maplist() functionality permitted the programmer to determine
which maps conflict with a given lhs.  (Currently, mapcheck
gives the rhs only, and there's no easy way to determine the
corresponding lhs.)

Sincerely,
Brett S.


>
> One of the logical variants would be `:map <push> {lhs}
> {new-rhs}`/`:unmap <push> {lhs}`+`:map <pop> {lhs}`, but this is hard
> to implement and is rather limited, though less limited then
> enable/disable everything variant.
>
> I would instead suggest a function mappings_dump()/mappings_add():
> first is similar to `nvim[_buf]_get_keymap` and should dump all
> mappings as a list of maparg()-like dictionaries. Second should define
> mappings being given a list of them. Of course, this means that
> dictionaries need to be fixed to allow correctly saving/restoring.
>
> The advantages:
>
> 1. Easier to implement. Code for creating a maparg() dictionary is
> already there, iterating over all mappings is not a problem. Results
> needs to be incompatible with maparg() or use additional keys though:
> e.g. Neovim altered the contents of `noremap` and `buffer` keys: first
> is now 0, 1 or 2 (you can’t correctly restore a mapping if you can’t
> distinguish `map <script>` and `noremap`) and second is a buffer
> number or zero.
> 2. More flexible: you can save and restore everything, push or pop
> individual mappings, create a temporary mapping which is just like
> mapping X, but has `<Plug>(Translit3TemporaryMap)` lhs instead (to be
> returned from `<expr>` mappings in order to select either plugin
> behaviour or fall back to previously present user mapping instead).
>
>    I can imagine other usages enable/disable or push/pop could not
> achieve: generating configuration with mappings like :mkvimrc, but
> allows doing adjustments (parsing `:mkvimrc` output is not fun,
> especially if you want to be forward compatible), creating a plugin
> which analyses how often different mappings are used (need to copy all
> mappings to temporary then replace existing mappings with plugin
> ones).
> 3. This is also forward compatible: just need to state in the
> documentation that new significant keys may be added in the future to
> the dictionaries so they should be preserved.
>
>>
>> Big advantage is that if we evern add functionality to mappings, it will
>> keep working, while your save/restore pair probably fails.
>>
>> Ah, your later post goes in the same direction.
>>
>> --
>> DENNIS: Look,  strange women lying on their backs in ponds handing out
>>         swords ... that's no basis for a system of government.  Supreme
>>         executive power derives from a mandate from the masses, not from some
>>         farcical aquatic ceremony.
>>                  "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD
>>
>>  /// Bram Moolenaar -- [hidden email] -- 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    ///
>>
>> --
>> --
>> You received this message from the "vim_use" maillist.
>> Do not top-post! Type your reply below the text you are replying to.
>> For more information, visit http://www.vim.org/maillist.php
>>
>> ---
>> You received this message because you are subscribed to the Google Groups "vim_use" group.
>> To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
>> For more options, visit https://groups.google.com/d/optout.

--
--
You received this message from the "vim_use" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

---
You received this message because you are subscribed to the Google Groups "vim_use" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Bug/non-determinism in output of maparg() and map commands

brettstahlman
In reply to this post by Nikolay Aleksandrovich Pavlov
On Fri, May 26, 2017 at 5:24 PM, Nikolay Aleksandrovich Pavlov
<[hidden email]> wrote:

> 2017-05-27 0:32 GMT+03:00 Brett Stahlman <[hidden email]>:
>> On Fri, May 26, 2017 at 4:12 PM, Nikolay Aleksandrovich Pavlov
>> <[hidden email]> wrote:
>>> 2017-05-26 20:43 GMT+03:00 Bram Moolenaar <[hidden email]>:
>>>>
>>>> Brett Stahlman wrote:
>>>>
>>>>> >> On Tuesday, May 23, 2017 at 8:25:33 AM UTC-5, Brett Stahlman wrote:
>>>>> >> > On Tue, May 23, 2017 at 4:35 AM, Bram Moolenaar <[hidden email]> wrote:
>>>>> >> > >
>>>>> >> > > Brett Stahlman wrote:
>>>>> >> > >
>>>>> >> %--snip--%
>>>>> >> > >
>>>>> >> > > The best solution is probably to also add the raw rhs, with the terminal
>>>>> >> > > codes replaced.  This won't work when changing the terminal type, but
>>>>> >> > > that is very unlikely to happen.
>>>>> >> >
>>>>> >> > You mean adding a key such as "raw_rhs" to the dictionary returned by
>>>>> >> > maparg()? If so, then yes this would help, but there would still need to
>>>>> >> > be a way to determine lhs, which is currently even more ambiguous than
>>>>> >> > rhs. While it's true that I probably already have lhs if I'm calling
>>>>> >> > maparg(), I need a way to determine which lhs(s) is/are ambiguous with a
>>>>> >> > given lhs. Mapcheck() gives me only the rhs of the conflicting map. To
>>>>> >> > save and restore, I'd need to know the lhs in canonical form as well.
>>>>> >>
>>>>> >> Perhaps mapcheck() could take an optional arg requesting something more than a simple boolean return. When called with this extra arg, mapcheck() could return a conflicting/ambiguous lhs (or list thereof) in some canonical format (possibly determined by the value of the extra arg itself). As long as the format returned could be fed to maparg(), it would be possible to find conflicting mappings, remove them temporarily, and subsequently restore them...
>>>>> >
>>>>> > If you define a mapping you will want to know whether the mapping
>>>>> > already exists and needs to be restored.  For that you can use maparg(),
>>>>> > no need to use mapcheck().
>>>>> >
>>>>> > Not sure why you would want to remove "conflicting" mappings. Perhaps
>>>>> > when you map the ; key, and the user has ;x mapped?  Then you would need
>>>>> > a list.  Adding a maplist() function would be better than adding
>>>>> > arguments to mapcheck().
>>>>>
>>>>> Yes. Very much like that. I'm implementing a sort of transient mode, in
>>>>> which I'll "shadow" existing maps with very short (generally single
>>>>> character) mappings, which are expected to be ambiguous/conflicting with
>>>>> existing maps, and even builtin operators. Of course, when I exit the
>>>>> transient mode, I'd need to restore the mappings that were shadowed.
>>>>>
>>>>> The global and builtin maps are not a problem, since the transient maps use
>>>>> <buffer> and <nowait>; however, without parsing the output of one of the :map
>>>>> functions, I have no way of knowing which buf-local mappings will be ambiguous
>>>>> with the transient maps I'm defining. And parsing the :map output is
>>>>> problematic for the reasons already mentioned: e.g., no way to tell the
>>>>> difference between function key <F8> and the corresponding 4 characters. I'd
>>>>> actually considered taking some sort of iterative approach: e.g., trying all
>>>>> possible permutations of lhs as input to maparg() and testing the results, in
>>>>> an attempt to deduce the canonical form, but this would be extremely messy,
>>>>> and I don't even know whether it would be deterministic... The maplist()
>>>>> function you mentioned, if it returned all ambiguous left hand sides in
>>>>> canonical form, or even a list of the corresponding maparg()-style
>>>>> dictionaries, would be perfect. Of course, there would also need to be a way
>>>>> to get the rhs's canonical form: e.g., the extra "raw_rhs" key in the maparg()
>>>>> or maplist() dictionary.
>>>>
>>>> OK, so for this you would use maplist() to get the list of mappings to
>>>> disable, use maparg() to get the current mapping, clear the mapping, do
>>>> your stuff, then restore the cleared mappings.  You then need to make
>>>> sure you restore the mappings exactly as they were, even when your
>>>> "stuff" fails miserably.
>>>>
>>>> It's a lot easier if we would have a way to temporarily disable
>>>> mappings.  It's mostly the same as above, but you won't need to use
>>>> maparg() to get the current mapping and the restore operation.  Instead
>>>> you would disable instead of clear, and later re-enable instead of
>>>> restore.  Still need to make sure the re-enbling does happen, no change
>>>> in that part.
>>>
>>> Not sure I understood what exactly you suggest to disable/restore. All
>>> mappings at once with one command? I would actually disagree here: I
>>> need something similar for translit3, but it only remaps
>>> single-character mappings, leaving most of other user mappings alone.
>>> One mapping at a time? It would be good, but given that request is
>>> temporary remapping naming the functionality enable/disable looks
>>> strange. And there are still issues with determining {lhs}.
>>
>> No. Not all maps at once. As I understood it, the maplist() function
>> would return a list of map "handles", each corresponding to a specific
>> map. It would probably have optional args that let you request handles
>> for all maps, or all maps ambiguous/conflicting with a specific lhs,
>> or even all maps satisfying certain criteria: e.g., buf-local or
>> global. The enable/disable functions (and any other such interface
>> functions) would accept a map handle and would operate only on that
>> map. In addition to the enable/disable functions, I proposed adding
>> some sort of get_map_info() function, which would return the
>> information currently returned in the maparg() dict. Also useful would
>> be an "execute" function, which would allow you to invoke a map
>> programmatically through its handle, even if the map is currently
>> disabled or shadowed (e.g., by a buf-local map).
>
> This looks like being too complex for no real reason. Approach
> suggested by me simplifies C code and allows imitating features
> possible with your suggestion in pure VimL: disable is just `:unmap`,
> enable is `mappings_load()` (`add` is not a good verb), handle is one
> of the dictionaries, execute can be emulated by “define two temporary
> mappings: one to enter target mode, second is the original handle with
> lhs replaced, then do `:execute 'normal'
> "\<Plug>(EnterMode)\<Plug>(OriginalMap)”`. (`<Plug>(EnterMode)` is
> there to avoid problems due to user possibly remapping sequences to
> enter target mode, it should normally be something like `nnoremap
> <Plug>(EnterNormalMode) <Nop>`, `nnoremap <Plug>(EnterInsertMode) i`,
> etc.)

Hmmm... If the goal is to provide the missing functionality
with minimal additions to the current C source, perhaps your
way makes the most sense. Though I wouldn't go so far as to
qualify a clearer, cleaner and safer user interface as "no
real reason". As I see it, the advantage of the approach I
outlined is that you would specify the lhs and rhs key
sequences exactly once, in one of the formats currently
supported by Vim, and from that point on, you could manage the
map throughout its life-cycle without ever having to deal
with the key sequences again. With no need to pass the
sequences between functions, there would be no need for
intermediate/canonical formats, special escaping for
normal/execute, etc...

The <Plug>(EnterMode)<Plug>(OriginalMode) strategy for
executing maps feels a bit fragile, albeit more in keeping
with the original vi paradigm, in which scripts operate just
like a normal user who happens to type really quickly. But
over the course of the last few versions of Vim, VimL has come
a long way towards providing programmatic (functional) access
to the Vim internals. The hybrid approach you describe can
feel a bit kludgy at times: passing key sequences in and out
of functions, then having to escape them specially for use
with commands like :execute and :normal. I mean, I've grown
used to it, but I think you'd be hard pressed to make the case
that it's as clean and intuitive as a more functional
approach.

At any rate, I'm ok with either approach, provided that the
current limitations are addressed: namely, inability to
determine all conflicting/ambiguous lhs(s), and inability to
get lhs/rhs in deterministic/canonical form.

Sincerely,
Brett S.

>
>>
>> While this approach could be made completely generic, I can also see
>> that the <push>/<pop> mechanism could simplify certain use cases... Of
>> course, just making the lhs/rhs values deterministic (e.g., by using
>> the code used for mkvimrc) would definitely be an improvement,
>> especially if some sort of maplist() functionality permitted the
>> programmer to determine which maps conflict with a given lhs.
>> (Currently, mapcheck gives the rhs only, and there's no easy way to
>> determine the corresponding lhs.)
>>
>> Sincerely,
>> Brett S.
>>
>>>
>>> One of the logical variants would be `:map <push> {lhs}
>>> {new-rhs}`/`:unmap <push> {lhs}`+`:map <pop> {lhs}`, but this is hard
>>> to implement and is rather limited, though less limited then
>>> enable/disable everything variant.
>>>
>>> I would instead suggest a function mappings_dump()/mappings_add():
>>> first is similar to `nvim[_buf]_get_keymap` and should dump all
>>> mappings as a list of maparg()-like dictionaries. Second should define
>>> mappings being given a list of them. Of course, this means that
>>> dictionaries need to be fixed to allow correctly saving/restoring.
>>>
>>> The advantages:
>>>
>>> 1. Easier to implement. Code for creating a maparg() dictionary is
>>> already there, iterating over all mappings is not a problem. Results
>>> needs to be incompatible with maparg() or use additional keys though:
>>> e.g. Neovim altered the contents of `noremap` and `buffer` keys: first
>>> is now 0, 1 or 2 (you can’t correctly restore a mapping if you can’t
>>> distinguish `map <script>` and `noremap`) and second is a buffer
>>> number or zero.
>>> 2. More flexible: you can save and restore everything, push or pop
>>> individual mappings, create a temporary mapping which is just like
>>> mapping X, but has `<Plug>(Translit3TemporaryMap)` lhs instead (to be
>>> returned from `<expr>` mappings in order to select either plugin
>>> behaviour or fall back to previously present user mapping instead).
>>>
>>>    I can imagine other usages enable/disable or push/pop could not
>>> achieve: generating configuration with mappings like :mkvimrc, but
>>> allows doing adjustments (parsing `:mkvimrc` output is not fun,
>>> especially if you want to be forward compatible), creating a plugin
>>> which analyses how often different mappings are used (need to copy all
>>> mappings to temporary then replace existing mappings with plugin
>>> ones).
>>> 3. This is also forward compatible: just need to state in the
>>> documentation that new significant keys may be added in the future to
>>> the dictionaries so they should be preserved.
>>>
>>>>
>>>> Big advantage is that if we evern add functionality to mappings, it will
>>>> keep working, while your save/restore pair probably fails.
>>>>
>>>> Ah, your later post goes in the same direction.
>>>>
>>>> --
>>>> DENNIS: Look,  strange women lying on their backs in ponds handing out
>>>>         swords ... that's no basis for a system of government.  Supreme
>>>>         executive power derives from a mandate from the masses, not from some
>>>>         farcical aquatic ceremony.
>>>>                  "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD
>>>>
>>>>  /// Bram Moolenaar -- [hidden email] -- 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    ///
>>>>
>>>> --
>>>> --
>>>> You received this message from the "vim_use" maillist.
>>>> Do not top-post! Type your reply below the text you are replying to.
>>>> For more information, visit http://www.vim.org/maillist.php
>>>>
>>>> ---
>>>> You received this message because you are subscribed to the Google Groups "vim_use" group.
>>>> To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
>>>> For more options, visit https://groups.google.com/d/optout.

--
--
You received this message from the "vim_use" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

---
You received this message because you are subscribed to the Google Groups "vim_use" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Bug/non-determinism in output of maparg() and map commands

Bram Moolenaar
In reply to this post by Nikolay Aleksandrovich Pavlov

Nikolay Pavlov wrote:

> 2017-05-26 20:43 GMT+03:00 Bram Moolenaar <[hidden email]>:
> >
> > Brett Stahlman wrote:
> >
> >> >> On Tuesday, May 23, 2017 at 8:25:33 AM UTC-5, Brett Stahlman wrote:
> >> >> > On Tue, May 23, 2017 at 4:35 AM, Bram Moolenaar <[hidden email]> wrote:
> >> >> > >
> >> >> > > Brett Stahlman wrote:
> >> >> > >
> >> >> %--snip--%
> >> >> > >
> >> >> > > The best solution is probably to also add the raw rhs, with the terminal
> >> >> > > codes replaced.  This won't work when changing the terminal type, but
> >> >> > > that is very unlikely to happen.
> >> >> >
> >> >> > You mean adding a key such as "raw_rhs" to the dictionary returned by
> >> >> > maparg()? If so, then yes this would help, but there would still need to
> >> >> > be a way to determine lhs, which is currently even more ambiguous than
> >> >> > rhs. While it's true that I probably already have lhs if I'm calling
> >> >> > maparg(), I need a way to determine which lhs(s) is/are ambiguous with a
> >> >> > given lhs. Mapcheck() gives me only the rhs of the conflicting map. To
> >> >> > save and restore, I'd need to know the lhs in canonical form as well.
> >> >>
> >> >> Perhaps mapcheck() could take an optional arg requesting something more than a simple boolean return. When called with this extra arg, mapcheck() could return a conflicting/ambiguous lhs (or list thereof) in some canonical format (possibly determined by the value of the extra arg itself). As long as the format returned could be fed to maparg(), it would be possible to find conflicting mappings, remove them temporarily, and subsequently restore them...
> >> >
> >> > If you define a mapping you will want to know whether the mapping
> >> > already exists and needs to be restored.  For that you can use maparg(),
> >> > no need to use mapcheck().
> >> >
> >> > Not sure why you would want to remove "conflicting" mappings. Perhaps
> >> > when you map the ; key, and the user has ;x mapped?  Then you would need
> >> > a list.  Adding a maplist() function would be better than adding
> >> > arguments to mapcheck().
> >>
> >> Yes. Very much like that. I'm implementing a sort of transient mode, in
> >> which I'll "shadow" existing maps with very short (generally single
> >> character) mappings, which are expected to be ambiguous/conflicting with
> >> existing maps, and even builtin operators. Of course, when I exit the
> >> transient mode, I'd need to restore the mappings that were shadowed.
> >>
> >> The global and builtin maps are not a problem, since the transient maps use
> >> <buffer> and <nowait>; however, without parsing the output of one of the :map
> >> functions, I have no way of knowing which buf-local mappings will be ambiguous
> >> with the transient maps I'm defining. And parsing the :map output is
> >> problematic for the reasons already mentioned: e.g., no way to tell the
> >> difference between function key <F8> and the corresponding 4 characters. I'd
> >> actually considered taking some sort of iterative approach: e.g., trying all
> >> possible permutations of lhs as input to maparg() and testing the results, in
> >> an attempt to deduce the canonical form, but this would be extremely messy,
> >> and I don't even know whether it would be deterministic... The maplist()
> >> function you mentioned, if it returned all ambiguous left hand sides in
> >> canonical form, or even a list of the corresponding maparg()-style
> >> dictionaries, would be perfect. Of course, there would also need to be a way
> >> to get the rhs's canonical form: e.g., the extra "raw_rhs" key in the maparg()
> >> or maplist() dictionary.
> >
> > OK, so for this you would use maplist() to get the list of mappings to
> > disable, use maparg() to get the current mapping, clear the mapping, do
> > your stuff, then restore the cleared mappings.  You then need to make
> > sure you restore the mappings exactly as they were, even when your
> > "stuff" fails miserably.
> >
> > It's a lot easier if we would have a way to temporarily disable
> > mappings.  It's mostly the same as above, but you won't need to use
> > maparg() to get the current mapping and the restore operation.  Instead
> > you would disable instead of clear, and later re-enable instead of
> > restore.  Still need to make sure the re-enbling does happen, no change
> > in that part.
>
> Not sure I understood what exactly you suggest to disable/restore. All
> mappings at once with one command? I would actually disagree here: I
> need something similar for translit3, but it only remaps
> single-character mappings, leaving most of other user mappings alone.
> One mapping at a time? It would be good, but given that request is
> temporary remapping naming the functionality enable/disable looks
> strange. And there are still issues with determining {lhs}.

Let's use an example: Suppose a plugin has a special mode for entering
data (e.g. chemical formulas).  It would then map some keys, e.g. "a".
If the user already has a mapping for "a" it needs to be restored when
leaving the special mode.  If the user has mappings starting with "a" we
would like to disable those, to avoid the timeout waiting for the next
character.

We do not want to disable mappings that don't interfere, to maximise the
freedom for the user to use other mappings at the same time.

> One of the logical variants would be `:map <push> {lhs}
> {new-rhs}`/`:unmap <push> {lhs}`+`:map <pop> {lhs}`, but this is hard
> to implement and is rather limited, though less limited then
> enable/disable everything variant.

This quickly gets complicated if we need to take into account all the
possible modes a mapping can be used in.

> I would instead suggest a function mappings_dump()/mappings_add():
> first is similar to `nvim[_buf]_get_keymap` and should dump all
> mappings as a list of maparg()-like dictionaries. Second should define
> mappings being given a list of them. Of course, this means that
> dictionaries need to be fixed to allow correctly saving/restoring.
>
> The advantages:
>
> 1. Easier to implement. Code for creating a maparg() dictionary is
> already there, iterating over all mappings is not a problem. Results
> needs to be incompatible with maparg() or use additional keys though:
> e.g. Neovim altered the contents of `noremap` and `buffer` keys: first
> is now 0, 1 or 2 (you can’t correctly restore a mapping if you can’t
> distinguish `map <script>` and `noremap`) and second is a buffer
> number or zero.
> 2. More flexible: you can save and restore everything, push or pop
> individual mappings, create a temporary mapping which is just like
> mapping X, but has `<Plug>(Translit3TemporaryMap)` lhs instead (to be
> returned from `<expr>` mappings in order to select either plugin
> behaviour or fall back to previously present user mapping instead).
>
>    I can imagine other usages enable/disable or push/pop could not
> achieve: generating configuration with mappings like :mkvimrc, but
> allows doing adjustments (parsing `:mkvimrc` output is not fun,
> especially if you want to be forward compatible), creating a plugin
> which analyses how often different mappings are used (need to copy all
> mappings to temporary then replace existing mappings with plugin
> ones).
> 3. This is also forward compatible: just need to state in the
> documentation that new significant keys may be added in the future to
> the dictionaries so they should be preserved.

I don't see much use for this.  I can't think of a practical example how
a plugin manipulates mappings it didn't create itself or even knows what
they are for.

Another complication is that mappings can be added/removed by other
mappings and by autocommands.

Disabling and re-enabling mappings is definitely more efficient than
removing and adding-back mappings.

--
BLACK KNIGHT: I'm invincible!
ARTHUR:       You're a looney.
                 "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD

 /// Bram Moolenaar -- [hidden email] -- 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    ///

--
--
You received this message from the "vim_use" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

---
You received this message because you are subscribed to the Google Groups "vim_use" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Bug/non-determinism in output of maparg() and map commands

brettstahlman
On Sat, May 27, 2017 at 4:45 AM, Bram Moolenaar <[hidden email]> wrote:

>
> Nikolay Pavlov wrote:
>
>> 2017-05-26 20:43 GMT+03:00 Bram Moolenaar <[hidden email]>:
>> >
>> > Brett Stahlman wrote:
>> >
>> >> >> On Tuesday, May 23, 2017 at 8:25:33 AM UTC-5, Brett Stahlman wrote:
>> >> >> > On Tue, May 23, 2017 at 4:35 AM, Bram Moolenaar <[hidden email]> wrote:
>> >> >> > >
>> >> >> > > Brett Stahlman wrote:
>> >> >> > >
>> >> >> %--snip--%
>> >> >> > >
>> >> >> > > The best solution is probably to also add the raw rhs, with the terminal
>> >> >> > > codes replaced.  This won't work when changing the terminal type, but
>> >> >> > > that is very unlikely to happen.
>> >> >> >
>> >> >> > You mean adding a key such as "raw_rhs" to the dictionary returned by
>> >> >> > maparg()? If so, then yes this would help, but there would still need to
>> >> >> > be a way to determine lhs, which is currently even more ambiguous than
>> >> >> > rhs. While it's true that I probably already have lhs if I'm calling
>> >> >> > maparg(), I need a way to determine which lhs(s) is/are ambiguous with a
>> >> >> > given lhs. Mapcheck() gives me only the rhs of the conflicting map. To
>> >> >> > save and restore, I'd need to know the lhs in canonical form as well.
>> >> >>
>> >> >> Perhaps mapcheck() could take an optional arg requesting something more than a simple boolean return. When called with this extra arg, mapcheck() could return a conflicting/ambiguous lhs (or list thereof) in some canonical format (possibly determined by the value of the extra arg itself). As long as the format returned could be fed to maparg(), it would be possible to find conflicting mappings, remove them temporarily, and subsequently restore them...
>> >> >
>> >> > If you define a mapping you will want to know whether the mapping
>> >> > already exists and needs to be restored.  For that you can use maparg(),
>> >> > no need to use mapcheck().
>> >> >
>> >> > Not sure why you would want to remove "conflicting" mappings. Perhaps
>> >> > when you map the ; key, and the user has ;x mapped?  Then you would need
>> >> > a list.  Adding a maplist() function would be better than adding
>> >> > arguments to mapcheck().
>> >>
>> >> Yes. Very much like that. I'm implementing a sort of transient mode, in
>> >> which I'll "shadow" existing maps with very short (generally single
>> >> character) mappings, which are expected to be ambiguous/conflicting with
>> >> existing maps, and even builtin operators. Of course, when I exit the
>> >> transient mode, I'd need to restore the mappings that were shadowed.
>> >>
>> >> The global and builtin maps are not a problem, since the transient maps use
>> >> <buffer> and <nowait>; however, without parsing the output of one of the :map
>> >> functions, I have no way of knowing which buf-local mappings will be ambiguous
>> >> with the transient maps I'm defining. And parsing the :map output is
>> >> problematic for the reasons already mentioned: e.g., no way to tell the
>> >> difference between function key <F8> and the corresponding 4 characters. I'd
>> >> actually considered taking some sort of iterative approach: e.g., trying all
>> >> possible permutations of lhs as input to maparg() and testing the results, in
>> >> an attempt to deduce the canonical form, but this would be extremely messy,
>> >> and I don't even know whether it would be deterministic... The maplist()
>> >> function you mentioned, if it returned all ambiguous left hand sides in
>> >> canonical form, or even a list of the corresponding maparg()-style
>> >> dictionaries, would be perfect. Of course, there would also need to be a way
>> >> to get the rhs's canonical form: e.g., the extra "raw_rhs" key in the maparg()
>> >> or maplist() dictionary.
>> >
>> > OK, so for this you would use maplist() to get the list of mappings to
>> > disable, use maparg() to get the current mapping, clear the mapping, do
>> > your stuff, then restore the cleared mappings.  You then need to make
>> > sure you restore the mappings exactly as they were, even when your
>> > "stuff" fails miserably.
>> >
>> > It's a lot easier if we would have a way to temporarily disable
>> > mappings.  It's mostly the same as above, but you won't need to use
>> > maparg() to get the current mapping and the restore operation.  Instead
>> > you would disable instead of clear, and later re-enable instead of
>> > restore.  Still need to make sure the re-enbling does happen, no change
>> > in that part.
>>
>> Not sure I understood what exactly you suggest to disable/restore. All
>> mappings at once with one command? I would actually disagree here: I
>> need something similar for translit3, but it only remaps
>> single-character mappings, leaving most of other user mappings alone.
>> One mapping at a time? It would be good, but given that request is
>> temporary remapping naming the functionality enable/disable looks
>> strange. And there are still issues with determining {lhs}.
>
> Let's use an example: Suppose a plugin has a special mode for entering
> data (e.g. chemical formulas).  It would then map some keys, e.g. "a".
> If the user already has a mapping for "a" it needs to be restored when
> leaving the special mode.  If the user has mappings starting with "a" we
> would like to disable those, to avoid the timeout waiting for the next
> character.
>
> We do not want to disable mappings that don't interfere, to maximise the
> freedom for the user to use other mappings at the same time.

Exactly. In such cases, I shouldn't need to know or care what
keys the user mapped from/to, how they need to be escaped,
which combinations of modes the map is active in, or what the
map does. All that matters is that it conflicts with the one
I'm about to define, and thus needs to be saved/restored
somehow...

Hmm... About modes... What would be the granularity of the
operable units? E.g., for a mapping defined with :map, would
there be 1 or 3 (n,v,o) such units? I'm guessing the
implementation could be complicated by the need to allow
control at the mode level, but I haven't looked into it. The
only drawback to having only lhs-level granularity is that it
might sometimes force you to disable more than necessary:
e.g., when you're using :nmap to override a mapping defined
with :map, which conflicts in only 1 of its 3 modes.

Sincerely,
Brett Stahlman

>
>> One of the logical variants would be `:map <push> {lhs}
>> {new-rhs}`/`:unmap <push> {lhs}`+`:map <pop> {lhs}`, but this is hard
>> to implement and is rather limited, though less limited then
>> enable/disable everything variant.
>
> This quickly gets complicated if we need to take into account all the
> possible modes a mapping can be used in.
>
>> I would instead suggest a function mappings_dump()/mappings_add():
>> first is similar to `nvim[_buf]_get_keymap` and should dump all
>> mappings as a list of maparg()-like dictionaries. Second should define
>> mappings being given a list of them. Of course, this means that
>> dictionaries need to be fixed to allow correctly saving/restoring.
>>
>> The advantages:
>>
>> 1. Easier to implement. Code for creating a maparg() dictionary is
>> already there, iterating over all mappings is not a problem. Results
>> needs to be incompatible with maparg() or use additional keys though:
>> e.g. Neovim altered the contents of `noremap` and `buffer` keys: first
>> is now 0, 1 or 2 (you can’t correctly restore a mapping if you can’t
>> distinguish `map <script>` and `noremap`) and second is a buffer
>> number or zero.
>> 2. More flexible: you can save and restore everything, push or pop
>> individual mappings, create a temporary mapping which is just like
>> mapping X, but has `<Plug>(Translit3TemporaryMap)` lhs instead (to be
>> returned from `<expr>` mappings in order to select either plugin
>> behaviour or fall back to previously present user mapping instead).
>>
>>    I can imagine other usages enable/disable or push/pop could not
>> achieve: generating configuration with mappings like :mkvimrc, but
>> allows doing adjustments (parsing `:mkvimrc` output is not fun,
>> especially if you want to be forward compatible), creating a plugin
>> which analyses how often different mappings are used (need to copy all
>> mappings to temporary then replace existing mappings with plugin
>> ones).
>> 3. This is also forward compatible: just need to state in the
>> documentation that new significant keys may be added in the future to
>> the dictionaries so they should be preserved.
>
> I don't see much use for this.  I can't think of a practical example how
> a plugin manipulates mappings it didn't create itself or even knows what
> they are for.
>
> Another complication is that mappings can be added/removed by other
> mappings and by autocommands.
>
> Disabling and re-enabling mappings is definitely more efficient than
> removing and adding-back mappings.
>
> --
> BLACK KNIGHT: I'm invincible!
> ARTHUR:       You're a looney.
>                  "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD
>
>  /// Bram Moolenaar -- [hidden email] -- 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    ///

--
--
You received this message from the "vim_use" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

---
You received this message because you are subscribed to the Google Groups "vim_use" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Bug/non-determinism in output of maparg() and map commands

Nikolay Aleksandrovich Pavlov
In reply to this post by Bram Moolenaar
2017-05-27 12:45 GMT+03:00 Bram Moolenaar <[hidden email]>:

>
> Nikolay Pavlov wrote:
>
>> 2017-05-26 20:43 GMT+03:00 Bram Moolenaar <[hidden email]>:
>> >
>> > Brett Stahlman wrote:
>> >
>> >> >> On Tuesday, May 23, 2017 at 8:25:33 AM UTC-5, Brett Stahlman wrote:
>> >> >> > On Tue, May 23, 2017 at 4:35 AM, Bram Moolenaar <[hidden email]> wrote:
>> >> >> > >
>> >> >> > > Brett Stahlman wrote:
>> >> >> > >
>> >> >> %--snip--%
>> >> >> > >
>> >> >> > > The best solution is probably to also add the raw rhs, with the terminal
>> >> >> > > codes replaced.  This won't work when changing the terminal type, but
>> >> >> > > that is very unlikely to happen.
>> >> >> >
>> >> >> > You mean adding a key such as "raw_rhs" to the dictionary returned by
>> >> >> > maparg()? If so, then yes this would help, but there would still need to
>> >> >> > be a way to determine lhs, which is currently even more ambiguous than
>> >> >> > rhs. While it's true that I probably already have lhs if I'm calling
>> >> >> > maparg(), I need a way to determine which lhs(s) is/are ambiguous with a
>> >> >> > given lhs. Mapcheck() gives me only the rhs of the conflicting map. To
>> >> >> > save and restore, I'd need to know the lhs in canonical form as well.
>> >> >>
>> >> >> Perhaps mapcheck() could take an optional arg requesting something more than a simple boolean return. When called with this extra arg, mapcheck() could return a conflicting/ambiguous lhs (or list thereof) in some canonical format (possibly determined by the value of the extra arg itself). As long as the format returned could be fed to maparg(), it would be possible to find conflicting mappings, remove them temporarily, and subsequently restore them...
>> >> >
>> >> > If you define a mapping you will want to know whether the mapping
>> >> > already exists and needs to be restored.  For that you can use maparg(),
>> >> > no need to use mapcheck().
>> >> >
>> >> > Not sure why you would want to remove "conflicting" mappings. Perhaps
>> >> > when you map the ; key, and the user has ;x mapped?  Then you would need
>> >> > a list.  Adding a maplist() function would be better than adding
>> >> > arguments to mapcheck().
>> >>
>> >> Yes. Very much like that. I'm implementing a sort of transient mode, in
>> >> which I'll "shadow" existing maps with very short (generally single
>> >> character) mappings, which are expected to be ambiguous/conflicting with
>> >> existing maps, and even builtin operators. Of course, when I exit the
>> >> transient mode, I'd need to restore the mappings that were shadowed.
>> >>
>> >> The global and builtin maps are not a problem, since the transient maps use
>> >> <buffer> and <nowait>; however, without parsing the output of one of the :map
>> >> functions, I have no way of knowing which buf-local mappings will be ambiguous
>> >> with the transient maps I'm defining. And parsing the :map output is
>> >> problematic for the reasons already mentioned: e.g., no way to tell the
>> >> difference between function key <F8> and the corresponding 4 characters. I'd
>> >> actually considered taking some sort of iterative approach: e.g., trying all
>> >> possible permutations of lhs as input to maparg() and testing the results, in
>> >> an attempt to deduce the canonical form, but this would be extremely messy,
>> >> and I don't even know whether it would be deterministic... The maplist()
>> >> function you mentioned, if it returned all ambiguous left hand sides in
>> >> canonical form, or even a list of the corresponding maparg()-style
>> >> dictionaries, would be perfect. Of course, there would also need to be a way
>> >> to get the rhs's canonical form: e.g., the extra "raw_rhs" key in the maparg()
>> >> or maplist() dictionary.
>> >
>> > OK, so for this you would use maplist() to get the list of mappings to
>> > disable, use maparg() to get the current mapping, clear the mapping, do
>> > your stuff, then restore the cleared mappings.  You then need to make
>> > sure you restore the mappings exactly as they were, even when your
>> > "stuff" fails miserably.
>> >
>> > It's a lot easier if we would have a way to temporarily disable
>> > mappings.  It's mostly the same as above, but you won't need to use
>> > maparg() to get the current mapping and the restore operation.  Instead
>> > you would disable instead of clear, and later re-enable instead of
>> > restore.  Still need to make sure the re-enbling does happen, no change
>> > in that part.
>>
>> Not sure I understood what exactly you suggest to disable/restore. All
>> mappings at once with one command? I would actually disagree here: I
>> need something similar for translit3, but it only remaps
>> single-character mappings, leaving most of other user mappings alone.
>> One mapping at a time? It would be good, but given that request is
>> temporary remapping naming the functionality enable/disable looks
>> strange. And there are still issues with determining {lhs}.
>
> Let's use an example: Suppose a plugin has a special mode for entering
> data (e.g. chemical formulas).  It would then map some keys, e.g. "a".
> If the user already has a mapping for "a" it needs to be restored when
> leaving the special mode.  If the user has mappings starting with "a" we
> would like to disable those, to avoid the timeout waiting for the next
> character.
>
> We do not want to disable mappings that don't interfere, to maximise the
> freedom for the user to use other mappings at the same time.
>
>> One of the logical variants would be `:map <push> {lhs}
>> {new-rhs}`/`:unmap <push> {lhs}`+`:map <pop> {lhs}`, but this is hard
>> to implement and is rather limited, though less limited then
>> enable/disable everything variant.
>
> This quickly gets complicated if we need to take into account all the
> possible modes a mapping can be used in.
>
>> I would instead suggest a function mappings_dump()/mappings_add():
>> first is similar to `nvim[_buf]_get_keymap` and should dump all
>> mappings as a list of maparg()-like dictionaries. Second should define
>> mappings being given a list of them. Of course, this means that
>> dictionaries need to be fixed to allow correctly saving/restoring.
>>
>> The advantages:
>>
>> 1. Easier to implement. Code for creating a maparg() dictionary is
>> already there, iterating over all mappings is not a problem. Results
>> needs to be incompatible with maparg() or use additional keys though:
>> e.g. Neovim altered the contents of `noremap` and `buffer` keys: first
>> is now 0, 1 or 2 (you can’t correctly restore a mapping if you can’t
>> distinguish `map <script>` and `noremap`) and second is a buffer
>> number or zero.
>> 2. More flexible: you can save and restore everything, push or pop
>> individual mappings, create a temporary mapping which is just like
>> mapping X, but has `<Plug>(Translit3TemporaryMap)` lhs instead (to be
>> returned from `<expr>` mappings in order to select either plugin
>> behaviour or fall back to previously present user mapping instead).
>>
>>    I can imagine other usages enable/disable or push/pop could not
>> achieve: generating configuration with mappings like :mkvimrc, but
>> allows doing adjustments (parsing `:mkvimrc` output is not fun,
>> especially if you want to be forward compatible), creating a plugin
>> which analyses how often different mappings are used (need to copy all
>> mappings to temporary then replace existing mappings with plugin
>> ones).
>> 3. This is also forward compatible: just need to state in the
>> documentation that new significant keys may be added in the future to
>> the dictionaries so they should be preserved.
>
> I don't see much use for this.  I can't think of a practical example how
> a plugin manipulates mappings it didn't create itself or even knows what
> they are for.

Still Vim has :mkvimrc which does manipulate (dump) mappings from
third-party plugins. Also I need this functionality for some <expr>
mappings: if some condition is true (e.g. `>` is preceded by `-` (C,
completion) or transliteration mode was enabled, or transliteration
mode is enabled *and* character that does not start a new
transliteration sequence is a continuation of previous one) use plugin
mapping. If it is false, fall back to whatever was there previously,
including falling back to whatever mapping was there previously.

Also check https://github.com/neovim/neovim/issues/6123, this is the
issue backing Neovim nvim_get_keymap() API function.

>
> Another complication is that mappings can be added/removed by other
> mappings and by autocommands.

I do not see how this complication is relevant to the discussion. I.e.
I do not see how this complication should affect usage or
implementation of both proposed changes.

>
> Disabling and re-enabling mappings is definitely more efficient than
> removing and adding-back mappings.

And it is also definitely both harder to implement and less flexible.

>
> --
> BLACK KNIGHT: I'm invincible!
> ARTHUR:       You're a looney.
>                  "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD
>
>  /// Bram Moolenaar -- [hidden email] -- 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    ///

--
--
You received this message from the "vim_use" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

---
You received this message because you are subscribed to the Google Groups "vim_use" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Bug/non-determinism in output of maparg() and map commands

brettstahlman
On Sat, May 27, 2017 at 8:35 AM, Nikolay Aleksandrovich Pavlov
<[hidden email]> wrote:

> 2017-05-27 12:45 GMT+03:00 Bram Moolenaar <[hidden email]>:
>>
>> Nikolay Pavlov wrote:
>>
>>> 2017-05-26 20:43 GMT+03:00 Bram Moolenaar <[hidden email]>:
>>> >
>>> > Brett Stahlman wrote:
>>> >
>>> >> >> On Tuesday, May 23, 2017 at 8:25:33 AM UTC-5, Brett Stahlman wrote:
>>> >> >> > On Tue, May 23, 2017 at 4:35 AM, Bram Moolenaar <[hidden email]> wrote:
>>> >> >> > >
>>> >> >> > > Brett Stahlman wrote:
>>> >> >> > >
>>> >> >> %--snip--%
>>> >> >> > >
>>> >> >> > > The best solution is probably to also add the raw rhs, with the terminal
>>> >> >> > > codes replaced.  This won't work when changing the terminal type, but
>>> >> >> > > that is very unlikely to happen.
>>> >> >> >
>>> >> >> > You mean adding a key such as "raw_rhs" to the dictionary returned by
>>> >> >> > maparg()? If so, then yes this would help, but there would still need to
>>> >> >> > be a way to determine lhs, which is currently even more ambiguous than
>>> >> >> > rhs. While it's true that I probably already have lhs if I'm calling
>>> >> >> > maparg(), I need a way to determine which lhs(s) is/are ambiguous with a
>>> >> >> > given lhs. Mapcheck() gives me only the rhs of the conflicting map. To
>>> >> >> > save and restore, I'd need to know the lhs in canonical form as well.
>>> >> >>
>>> >> >> Perhaps mapcheck() could take an optional arg requesting something more than a simple boolean return. When called with this extra arg, mapcheck() could return a conflicting/ambiguous lhs (or list thereof) in some canonical format (possibly determined by the value of the extra arg itself). As long as the format returned could be fed to maparg(), it would be possible to find conflicting mappings, remove them temporarily, and subsequently restore them...
>>> >> >
>>> >> > If you define a mapping you will want to know whether the mapping
>>> >> > already exists and needs to be restored.  For that you can use maparg(),
>>> >> > no need to use mapcheck().
>>> >> >
>>> >> > Not sure why you would want to remove "conflicting" mappings. Perhaps
>>> >> > when you map the ; key, and the user has ;x mapped?  Then you would need
>>> >> > a list.  Adding a maplist() function would be better than adding
>>> >> > arguments to mapcheck().
>>> >>
>>> >> Yes. Very much like that. I'm implementing a sort of transient mode, in
>>> >> which I'll "shadow" existing maps with very short (generally single
>>> >> character) mappings, which are expected to be ambiguous/conflicting with
>>> >> existing maps, and even builtin operators. Of course, when I exit the
>>> >> transient mode, I'd need to restore the mappings that were shadowed.
>>> >>
>>> >> The global and builtin maps are not a problem, since the transient maps use
>>> >> <buffer> and <nowait>; however, without parsing the output of one of the :map
>>> >> functions, I have no way of knowing which buf-local mappings will be ambiguous
>>> >> with the transient maps I'm defining. And parsing the :map output is
>>> >> problematic for the reasons already mentioned: e.g., no way to tell the
>>> >> difference between function key <F8> and the corresponding 4 characters. I'd
>>> >> actually considered taking some sort of iterative approach: e.g., trying all
>>> >> possible permutations of lhs as input to maparg() and testing the results, in
>>> >> an attempt to deduce the canonical form, but this would be extremely messy,
>>> >> and I don't even know whether it would be deterministic... The maplist()
>>> >> function you mentioned, if it returned all ambiguous left hand sides in
>>> >> canonical form, or even a list of the corresponding maparg()-style
>>> >> dictionaries, would be perfect. Of course, there would also need to be a way
>>> >> to get the rhs's canonical form: e.g., the extra "raw_rhs" key in the maparg()
>>> >> or maplist() dictionary.
>>> >
>>> > OK, so for this you would use maplist() to get the list of mappings to
>>> > disable, use maparg() to get the current mapping, clear the mapping, do
>>> > your stuff, then restore the cleared mappings.  You then need to make
>>> > sure you restore the mappings exactly as they were, even when your
>>> > "stuff" fails miserably.
>>> >
>>> > It's a lot easier if we would have a way to temporarily disable
>>> > mappings.  It's mostly the same as above, but you won't need to use
>>> > maparg() to get the current mapping and the restore operation.  Instead
>>> > you would disable instead of clear, and later re-enable instead of
>>> > restore.  Still need to make sure the re-enbling does happen, no change
>>> > in that part.
>>>
>>> Not sure I understood what exactly you suggest to disable/restore. All
>>> mappings at once with one command? I would actually disagree here: I
>>> need something similar for translit3, but it only remaps
>>> single-character mappings, leaving most of other user mappings alone.
>>> One mapping at a time? It would be good, but given that request is
>>> temporary remapping naming the functionality enable/disable looks
>>> strange. And there are still issues with determining {lhs}.
>>
>> Let's use an example: Suppose a plugin has a special mode for entering
>> data (e.g. chemical formulas).  It would then map some keys, e.g. "a".
>> If the user already has a mapping for "a" it needs to be restored when
>> leaving the special mode.  If the user has mappings starting with "a" we
>> would like to disable those, to avoid the timeout waiting for the next
>> character.
>>
>> We do not want to disable mappings that don't interfere, to maximise the
>> freedom for the user to use other mappings at the same time.
>>
>>> One of the logical variants would be `:map <push> {lhs}
>>> {new-rhs}`/`:unmap <push> {lhs}`+`:map <pop> {lhs}`, but this is hard
>>> to implement and is rather limited, though less limited then
>>> enable/disable everything variant.
>>
>> This quickly gets complicated if we need to take into account all the
>> possible modes a mapping can be used in.
>>
>>> I would instead suggest a function mappings_dump()/mappings_add():
>>> first is similar to `nvim[_buf]_get_keymap` and should dump all
>>> mappings as a list of maparg()-like dictionaries. Second should define
>>> mappings being given a list of them. Of course, this means that
>>> dictionaries need to be fixed to allow correctly saving/restoring.
>>>
>>> The advantages:
>>>
>>> 1. Easier to implement. Code for creating a maparg() dictionary is
>>> already there, iterating over all mappings is not a problem. Results
>>> needs to be incompatible with maparg() or use additional keys though:
>>> e.g. Neovim altered the contents of `noremap` and `buffer` keys: first
>>> is now 0, 1 or 2 (you can’t correctly restore a mapping if you can’t
>>> distinguish `map <script>` and `noremap`) and second is a buffer
>>> number or zero.
>>> 2. More flexible: you can save and restore everything, push or pop
>>> individual mappings, create a temporary mapping which is just like
>>> mapping X, but has `<Plug>(Translit3TemporaryMap)` lhs instead (to be
>>> returned from `<expr>` mappings in order to select either plugin
>>> behaviour or fall back to previously present user mapping instead).
>>>
>>>    I can imagine other usages enable/disable or push/pop could not
>>> achieve: generating configuration with mappings like :mkvimrc, but
>>> allows doing adjustments (parsing `:mkvimrc` output is not fun,
>>> especially if you want to be forward compatible), creating a plugin
>>> which analyses how often different mappings are used (need to copy all
>>> mappings to temporary then replace existing mappings with plugin
>>> ones).
>>> 3. This is also forward compatible: just need to state in the
>>> documentation that new significant keys may be added in the future to
>>> the dictionaries so they should be preserved.
>>
>> I don't see much use for this.  I can't think of a practical example how
>> a plugin manipulates mappings it didn't create itself or even knows what
>> they are for.
>
> Still Vim has :mkvimrc which does manipulate (dump) mappings from
> third-party plugins. Also I need this functionality for some <expr>
> mappings: if some condition is true (e.g. `>` is preceded by `-` (C,
> completion) or transliteration mode was enabled, or transliteration
> mode is enabled *and* character that does not start a new
> transliteration sequence is a continuation of previous one) use plugin
> mapping. If it is false, fall back to whatever was there previously,
> including falling back to whatever mapping was there previously.
>
> Also check https://github.com/neovim/neovim/issues/6123, this is the
> issue backing Neovim nvim_get_keymap() API function.
>
>>
>> Another complication is that mappings can be added/removed by other
>> mappings and by autocommands.
>
> I do not see how this complication is relevant to the discussion. I.e.
> I do not see how this complication should affect usage or
> implementation of both proposed changes.
>
>>
>> Disabling and re-enabling mappings is definitely more efficient than
>> removing and adding-back mappings.
>
> And it is also definitely both harder to implement and less flexible.

Harder to implement, perhaps, but not necessarily less
flexible. Though the discussion thus far has centered mostly
on enable/disable functionality, there's nothing about the map
handle interface that limits it to this. It could support
query and execute functions, for instance. For cases in which
you wish to keep the original behavior, but need to "wrap" it
somehow, you could use the map handle to attach prolog/epilog
callback functions to a map. Presumably, such callback
functions (which could be either lambdas or funcrefs) would
accept an argument that allowed them to obtain information
about the original map, possibly even its exact lhs and rhs.
The prolog callback would be even more useful if Vim provided
a way (e.g., nonzero return) for it to abort the original map.

Hmm... This may be overkill, but it might even be possible to
support the idea of a "virtual map handle": i.e., a handle not
to a specific map, but to a *set* of maps matching certain
criteria: e.g., <buffer>, <expr>, maps matching a mode mask,
maps starting with specific char(s), etc...  Once such a
virtual handle had been obtained, a single call would suffice
to enable/disable, or even attach callbacks to all maps in the
set. Of course, some operations (e.g., execute) would be
permitted only on non-virtual (single map) handles.

Sincerely,
Brett Stahlman

>
>>
>> --
>> BLACK KNIGHT: I'm invincible!
>> ARTHUR:       You're a looney.
>>                  "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD
>>
>>  /// Bram Moolenaar -- [hidden email] -- 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    ///

--
--
You received this message from the "vim_use" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

---
You received this message because you are subscribed to the Google Groups "vim_use" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Bug/non-determinism in output of maparg() and map commands

Nikolay Aleksandrovich Pavlov
2017-05-27 18:02 GMT+03:00 Brett Stahlman <[hidden email]>:

> On Sat, May 27, 2017 at 8:35 AM, Nikolay Aleksandrovich Pavlov
> <[hidden email]> wrote:
>> 2017-05-27 12:45 GMT+03:00 Bram Moolenaar <[hidden email]>:
>>>
>>> Nikolay Pavlov wrote:
>>>
>>>> 2017-05-26 20:43 GMT+03:00 Bram Moolenaar <[hidden email]>:
>>>> >
>>>> > Brett Stahlman wrote:
>>>> >
>>>> >> >> On Tuesday, May 23, 2017 at 8:25:33 AM UTC-5, Brett Stahlman wrote:
>>>> >> >> > On Tue, May 23, 2017 at 4:35 AM, Bram Moolenaar <[hidden email]> wrote:
>>>> >> >> > >
>>>> >> >> > > Brett Stahlman wrote:
>>>> >> >> > >
>>>> >> >> %--snip--%
>>>> >> >> > >
>>>> >> >> > > The best solution is probably to also add the raw rhs, with the terminal
>>>> >> >> > > codes replaced.  This won't work when changing the terminal type, but
>>>> >> >> > > that is very unlikely to happen.
>>>> >> >> >
>>>> >> >> > You mean adding a key such as "raw_rhs" to the dictionary returned by
>>>> >> >> > maparg()? If so, then yes this would help, but there would still need to
>>>> >> >> > be a way to determine lhs, which is currently even more ambiguous than
>>>> >> >> > rhs. While it's true that I probably already have lhs if I'm calling
>>>> >> >> > maparg(), I need a way to determine which lhs(s) is/are ambiguous with a
>>>> >> >> > given lhs. Mapcheck() gives me only the rhs of the conflicting map. To
>>>> >> >> > save and restore, I'd need to know the lhs in canonical form as well.
>>>> >> >>
>>>> >> >> Perhaps mapcheck() could take an optional arg requesting something more than a simple boolean return. When called with this extra arg, mapcheck() could return a conflicting/ambiguous lhs (or list thereof) in some canonical format (possibly determined by the value of the extra arg itself). As long as the format returned could be fed to maparg(), it would be possible to find conflicting mappings, remove them temporarily, and subsequently restore them...
>>>> >> >
>>>> >> > If you define a mapping you will want to know whether the mapping
>>>> >> > already exists and needs to be restored.  For that you can use maparg(),
>>>> >> > no need to use mapcheck().
>>>> >> >
>>>> >> > Not sure why you would want to remove "conflicting" mappings. Perhaps
>>>> >> > when you map the ; key, and the user has ;x mapped?  Then you would need
>>>> >> > a list.  Adding a maplist() function would be better than adding
>>>> >> > arguments to mapcheck().
>>>> >>
>>>> >> Yes. Very much like that. I'm implementing a sort of transient mode, in
>>>> >> which I'll "shadow" existing maps with very short (generally single
>>>> >> character) mappings, which are expected to be ambiguous/conflicting with
>>>> >> existing maps, and even builtin operators. Of course, when I exit the
>>>> >> transient mode, I'd need to restore the mappings that were shadowed.
>>>> >>
>>>> >> The global and builtin maps are not a problem, since the transient maps use
>>>> >> <buffer> and <nowait>; however, without parsing the output of one of the :map
>>>> >> functions, I have no way of knowing which buf-local mappings will be ambiguous
>>>> >> with the transient maps I'm defining. And parsing the :map output is
>>>> >> problematic for the reasons already mentioned: e.g., no way to tell the
>>>> >> difference between function key <F8> and the corresponding 4 characters. I'd
>>>> >> actually considered taking some sort of iterative approach: e.g., trying all
>>>> >> possible permutations of lhs as input to maparg() and testing the results, in
>>>> >> an attempt to deduce the canonical form, but this would be extremely messy,
>>>> >> and I don't even know whether it would be deterministic... The maplist()
>>>> >> function you mentioned, if it returned all ambiguous left hand sides in
>>>> >> canonical form, or even a list of the corresponding maparg()-style
>>>> >> dictionaries, would be perfect. Of course, there would also need to be a way
>>>> >> to get the rhs's canonical form: e.g., the extra "raw_rhs" key in the maparg()
>>>> >> or maplist() dictionary.
>>>> >
>>>> > OK, so for this you would use maplist() to get the list of mappings to
>>>> > disable, use maparg() to get the current mapping, clear the mapping, do
>>>> > your stuff, then restore the cleared mappings.  You then need to make
>>>> > sure you restore the mappings exactly as they were, even when your
>>>> > "stuff" fails miserably.
>>>> >
>>>> > It's a lot easier if we would have a way to temporarily disable
>>>> > mappings.  It's mostly the same as above, but you won't need to use
>>>> > maparg() to get the current mapping and the restore operation.  Instead
>>>> > you would disable instead of clear, and later re-enable instead of
>>>> > restore.  Still need to make sure the re-enbling does happen, no change
>>>> > in that part.
>>>>
>>>> Not sure I understood what exactly you suggest to disable/restore. All
>>>> mappings at once with one command? I would actually disagree here: I
>>>> need something similar for translit3, but it only remaps
>>>> single-character mappings, leaving most of other user mappings alone.
>>>> One mapping at a time? It would be good, but given that request is
>>>> temporary remapping naming the functionality enable/disable looks
>>>> strange. And there are still issues with determining {lhs}.
>>>
>>> Let's use an example: Suppose a plugin has a special mode for entering
>>> data (e.g. chemical formulas).  It would then map some keys, e.g. "a".
>>> If the user already has a mapping for "a" it needs to be restored when
>>> leaving the special mode.  If the user has mappings starting with "a" we
>>> would like to disable those, to avoid the timeout waiting for the next
>>> character.
>>>
>>> We do not want to disable mappings that don't interfere, to maximise the
>>> freedom for the user to use other mappings at the same time.
>>>
>>>> One of the logical variants would be `:map <push> {lhs}
>>>> {new-rhs}`/`:unmap <push> {lhs}`+`:map <pop> {lhs}`, but this is hard
>>>> to implement and is rather limited, though less limited then
>>>> enable/disable everything variant.
>>>
>>> This quickly gets complicated if we need to take into account all the
>>> possible modes a mapping can be used in.
>>>
>>>> I would instead suggest a function mappings_dump()/mappings_add():
>>>> first is similar to `nvim[_buf]_get_keymap` and should dump all
>>>> mappings as a list of maparg()-like dictionaries. Second should define
>>>> mappings being given a list of them. Of course, this means that
>>>> dictionaries need to be fixed to allow correctly saving/restoring.
>>>>
>>>> The advantages:
>>>>
>>>> 1. Easier to implement. Code for creating a maparg() dictionary is
>>>> already there, iterating over all mappings is not a problem. Results
>>>> needs to be incompatible with maparg() or use additional keys though:
>>>> e.g. Neovim altered the contents of `noremap` and `buffer` keys: first
>>>> is now 0, 1 or 2 (you can’t correctly restore a mapping if you can’t
>>>> distinguish `map <script>` and `noremap`) and second is a buffer
>>>> number or zero.
>>>> 2. More flexible: you can save and restore everything, push or pop
>>>> individual mappings, create a temporary mapping which is just like
>>>> mapping X, but has `<Plug>(Translit3TemporaryMap)` lhs instead (to be
>>>> returned from `<expr>` mappings in order to select either plugin
>>>> behaviour or fall back to previously present user mapping instead).
>>>>
>>>>    I can imagine other usages enable/disable or push/pop could not
>>>> achieve: generating configuration with mappings like :mkvimrc, but
>>>> allows doing adjustments (parsing `:mkvimrc` output is not fun,
>>>> especially if you want to be forward compatible), creating a plugin
>>>> which analyses how often different mappings are used (need to copy all
>>>> mappings to temporary then replace existing mappings with plugin
>>>> ones).
>>>> 3. This is also forward compatible: just need to state in the
>>>> documentation that new significant keys may be added in the future to
>>>> the dictionaries so they should be preserved.
>>>
>>> I don't see much use for this.  I can't think of a practical example how
>>> a plugin manipulates mappings it didn't create itself or even knows what
>>> they are for.
>>
>> Still Vim has :mkvimrc which does manipulate (dump) mappings from
>> third-party plugins. Also I need this functionality for some <expr>
>> mappings: if some condition is true (e.g. `>` is preceded by `-` (C,
>> completion) or transliteration mode was enabled, or transliteration
>> mode is enabled *and* character that does not start a new
>> transliteration sequence is a continuation of previous one) use plugin
>> mapping. If it is false, fall back to whatever was there previously,
>> including falling back to whatever mapping was there previously.
>>
>> Also check https://github.com/neovim/neovim/issues/6123, this is the
>> issue backing Neovim nvim_get_keymap() API function.
>>
>>>
>>> Another complication is that mappings can be added/removed by other
>>> mappings and by autocommands.
>>
>> I do not see how this complication is relevant to the discussion. I.e.
>> I do not see how this complication should affect usage or
>> implementation of both proposed changes.
>>
>>>
>>> Disabling and re-enabling mappings is definitely more efficient than
>>> removing and adding-back mappings.
>>
>> And it is also definitely both harder to implement and less flexible.
>
> Harder to implement, perhaps, but not necessarily less
> flexible. Though the discussion thus far has centered mostly
> on enable/disable functionality, there's nothing about the map
> handle interface that limits it to this. It could support
> query and execute functions, for instance. For cases in which
> you wish to keep the original behavior, but need to "wrap" it
> somehow, you could use the map handle to attach prolog/epilog
> callback functions to a map. Presumably, such callback
> functions (which could be either lambdas or funcrefs) would
> accept an argument that allowed them to obtain information
> about the original map, possibly even its exact lhs and rhs.
> The prolog callback would be even more useful if Vim provided
> a way (e.g., nonzero return) for it to abort the original map.

Enable, disable, query, execute plus two callbacks. *Four* functions
and two callbacks in place of just two simple functions, mostly using
the functionality that is already there. Five if you remember about
:mkvimrc and that somebody may want to replace that on top of new API:
query will need a mirror function for creating a mapping then.

This is going to introduce a big amount of bugs just to add the
flexibility which is naturally available through a much simpler
approach. Emulating everything you mention on top of current VimL
state plus mappings_dump()/mappings_load() / (mappings_clear()*) is
not going to make plugins considerably slower (as long as you can
operate on lists and use `map()`/`filter()`/etc: main VimL
optimization principle is “the less Ex commands the faster the code”)
and I do not see any other benefits, except for “with some handles
implementation it may be slightly easier to pinpoint third-party
plugins’ bugs”.

* Found an issue in my proposal: `:execute 'unmap'` would not be easy
or efficient to use, so additionally need either `mappings_clear({list
to clear})` or make `mappings_load()` unmap mappings when rhs key is
missing.

>
> Hmm... This may be overkill, but it might even be possible to
> support the idea of a "virtual map handle": i.e., a handle not
> to a specific map, but to a *set* of maps matching certain
> criteria: e.g., <buffer>, <expr>, maps matching a mode mask,
> maps starting with specific char(s), etc...  Once such a
> virtual handle had been obtained, a single call would suffice
> to enable/disable, or even attach callbacks to all maps in the
> set. Of course, some operations (e.g., execute) would be
> permitted only on non-virtual (single map) handles.

And this is just mappings_dump() + filter() with my approach without
any need to invent a new DSL to describe criterias (or not invent DSL,
but use VimL expressions which would be just as efficient as
filter()). If I got it right then plus some way to attach callbacks to
“new mapping defined” event to keep “callback attached” state.

>
> Sincerely,
> Brett Stahlman
>
>>
>>>
>>> --
>>> BLACK KNIGHT: I'm invincible!
>>> ARTHUR:       You're a looney.
>>>                  "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD
>>>
>>>  /// Bram Moolenaar -- [hidden email] -- 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    ///

--
--
You received this message from the "vim_use" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

---
You received this message because you are subscribed to the Google Groups "vim_use" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Bug/non-determinism in output of maparg() and map commands

brettstahlman
On Sat, May 27, 2017 at 10:39 AM, Nikolay Aleksandrovich Pavlov
<[hidden email]> wrote:

> 2017-05-27 18:02 GMT+03:00 Brett Stahlman <[hidden email]>:
>> On Sat, May 27, 2017 at 8:35 AM, Nikolay Aleksandrovich Pavlov
>> <[hidden email]> wrote:
>>> 2017-05-27 12:45 GMT+03:00 Bram Moolenaar <[hidden email]>:
>>>>
>>>> Nikolay Pavlov wrote:
>>>>
>>>>> 2017-05-26 20:43 GMT+03:00 Bram Moolenaar <[hidden email]>:
>>>>> >
>>>>> > Brett Stahlman wrote:
>>>>> >
>>>>> >> >> On Tuesday, May 23, 2017 at 8:25:33 AM UTC-5, Brett Stahlman wrote:
>>>>> >> >> > On Tue, May 23, 2017 at 4:35 AM, Bram Moolenaar <[hidden email]> wrote:
>>>>> >> >> > >
>>>>> >> >> > > Brett Stahlman wrote:
>>>>> >> >> > >
>>>>> >> >> %--snip--%
>>>>> >> >> > >
>>>>> >> >> > > The best solution is probably to also add the raw rhs, with the terminal
>>>>> >> >> > > codes replaced.  This won't work when changing the terminal type, but
>>>>> >> >> > > that is very unlikely to happen.
>>>>> >> >> >
>>>>> >> >> > You mean adding a key such as "raw_rhs" to the dictionary returned by
>>>>> >> >> > maparg()? If so, then yes this would help, but there would still need to
>>>>> >> >> > be a way to determine lhs, which is currently even more ambiguous than
>>>>> >> >> > rhs. While it's true that I probably already have lhs if I'm calling
>>>>> >> >> > maparg(), I need a way to determine which lhs(s) is/are ambiguous with a
>>>>> >> >> > given lhs. Mapcheck() gives me only the rhs of the conflicting map. To
>>>>> >> >> > save and restore, I'd need to know the lhs in canonical form as well.
>>>>> >> >>
>>>>> >> >> Perhaps mapcheck() could take an optional arg requesting something more than a simple boolean return. When called with this extra arg, mapcheck() could return a conflicting/ambiguous lhs (or list thereof) in some canonical format (possibly determined by the value of the extra arg itself). As long as the format returned could be fed to maparg(), it would be possible to find conflicting mappings, remove them temporarily, and subsequently restore them...
>>>>> >> >
>>>>> >> > If you define a mapping you will want to know whether the mapping
>>>>> >> > already exists and needs to be restored.  For that you can use maparg(),
>>>>> >> > no need to use mapcheck().
>>>>> >> >
>>>>> >> > Not sure why you would want to remove "conflicting" mappings. Perhaps
>>>>> >> > when you map the ; key, and the user has ;x mapped?  Then you would need
>>>>> >> > a list.  Adding a maplist() function would be better than adding
>>>>> >> > arguments to mapcheck().
>>>>> >>
>>>>> >> Yes. Very much like that. I'm implementing a sort of transient mode, in
>>>>> >> which I'll "shadow" existing maps with very short (generally single
>>>>> >> character) mappings, which are expected to be ambiguous/conflicting with
>>>>> >> existing maps, and even builtin operators. Of course, when I exit the
>>>>> >> transient mode, I'd need to restore the mappings that were shadowed.
>>>>> >>
>>>>> >> The global and builtin maps are not a problem, since the transient maps use
>>>>> >> <buffer> and <nowait>; however, without parsing the output of one of the :map
>>>>> >> functions, I have no way of knowing which buf-local mappings will be ambiguous
>>>>> >> with the transient maps I'm defining. And parsing the :map output is
>>>>> >> problematic for the reasons already mentioned: e.g., no way to tell the
>>>>> >> difference between function key <F8> and the corresponding 4 characters. I'd
>>>>> >> actually considered taking some sort of iterative approach: e.g., trying all
>>>>> >> possible permutations of lhs as input to maparg() and testing the results, in
>>>>> >> an attempt to deduce the canonical form, but this would be extremely messy,
>>>>> >> and I don't even know whether it would be deterministic... The maplist()
>>>>> >> function you mentioned, if it returned all ambiguous left hand sides in
>>>>> >> canonical form, or even a list of the corresponding maparg()-style
>>>>> >> dictionaries, would be perfect. Of course, there would also need to be a way
>>>>> >> to get the rhs's canonical form: e.g., the extra "raw_rhs" key in the maparg()
>>>>> >> or maplist() dictionary.
>>>>> >
>>>>> > OK, so for this you would use maplist() to get the list of mappings to
>>>>> > disable, use maparg() to get the current mapping, clear the mapping, do
>>>>> > your stuff, then restore the cleared mappings.  You then need to make
>>>>> > sure you restore the mappings exactly as they were, even when your
>>>>> > "stuff" fails miserably.
>>>>> >
>>>>> > It's a lot easier if we would have a way to temporarily disable
>>>>> > mappings.  It's mostly the same as above, but you won't need to use
>>>>> > maparg() to get the current mapping and the restore operation.  Instead
>>>>> > you would disable instead of clear, and later re-enable instead of
>>>>> > restore.  Still need to make sure the re-enbling does happen, no change
>>>>> > in that part.
>>>>>
>>>>> Not sure I understood what exactly you suggest to disable/restore. All
>>>>> mappings at once with one command? I would actually disagree here: I
>>>>> need something similar for translit3, but it only remaps
>>>>> single-character mappings, leaving most of other user mappings alone.
>>>>> One mapping at a time? It would be good, but given that request is
>>>>> temporary remapping naming the functionality enable/disable looks
>>>>> strange. And there are still issues with determining {lhs}.
>>>>
>>>> Let's use an example: Suppose a plugin has a special mode for entering
>>>> data (e.g. chemical formulas).  It would then map some keys, e.g. "a".
>>>> If the user already has a mapping for "a" it needs to be restored when
>>>> leaving the special mode.  If the user has mappings starting with "a" we
>>>> would like to disable those, to avoid the timeout waiting for the next
>>>> character.
>>>>
>>>> We do not want to disable mappings that don't interfere, to maximise the
>>>> freedom for the user to use other mappings at the same time.
>>>>
>>>>> One of the logical variants would be `:map <push> {lhs}
>>>>> {new-rhs}`/`:unmap <push> {lhs}`+`:map <pop> {lhs}`, but this is hard
>>>>> to implement and is rather limited, though less limited then
>>>>> enable/disable everything variant.
>>>>
>>>> This quickly gets complicated if we need to take into account all the
>>>> possible modes a mapping can be used in.
>>>>
>>>>> I would instead suggest a function mappings_dump()/mappings_add():
>>>>> first is similar to `nvim[_buf]_get_keymap` and should dump all
>>>>> mappings as a list of maparg()-like dictionaries. Second should define
>>>>> mappings being given a list of them. Of course, this means that
>>>>> dictionaries need to be fixed to allow correctly saving/restoring.
>>>>>
>>>>> The advantages:
>>>>>
>>>>> 1. Easier to implement. Code for creating a maparg() dictionary is
>>>>> already there, iterating over all mappings is not a problem. Results
>>>>> needs to be incompatible with maparg() or use additional keys though:
>>>>> e.g. Neovim altered the contents of `noremap` and `buffer` keys: first
>>>>> is now 0, 1 or 2 (you can’t correctly restore a mapping if you can’t
>>>>> distinguish `map <script>` and `noremap`) and second is a buffer
>>>>> number or zero.
>>>>> 2. More flexible: you can save and restore everything, push or pop
>>>>> individual mappings, create a temporary mapping which is just like
>>>>> mapping X, but has `<Plug>(Translit3TemporaryMap)` lhs instead (to be
>>>>> returned from `<expr>` mappings in order to select either plugin
>>>>> behaviour or fall back to previously present user mapping instead).
>>>>>
>>>>>    I can imagine other usages enable/disable or push/pop could not
>>>>> achieve: generating configuration with mappings like :mkvimrc, but
>>>>> allows doing adjustments (parsing `:mkvimrc` output is not fun,
>>>>> especially if you want to be forward compatible), creating a plugin
>>>>> which analyses how often different mappings are used (need to copy all
>>>>> mappings to temporary then replace existing mappings with plugin
>>>>> ones).
>>>>> 3. This is also forward compatible: just need to state in the
>>>>> documentation that new significant keys may be added in the future to
>>>>> the dictionaries so they should be preserved.
>>>>
>>>> I don't see much use for this.  I can't think of a practical example how
>>>> a plugin manipulates mappings it didn't create itself or even knows what
>>>> they are for.
>>>
>>> Still Vim has :mkvimrc which does manipulate (dump) mappings from
>>> third-party plugins. Also I need this functionality for some <expr>
>>> mappings: if some condition is true (e.g. `>` is preceded by `-` (C,
>>> completion) or transliteration mode was enabled, or transliteration
>>> mode is enabled *and* character that does not start a new
>>> transliteration sequence is a continuation of previous one) use plugin
>>> mapping. If it is false, fall back to whatever was there previously,
>>> including falling back to whatever mapping was there previously.
>>>
>>> Also check https://github.com/neovim/neovim/issues/6123, this is the
>>> issue backing Neovim nvim_get_keymap() API function.
>>>
>>>>
>>>> Another complication is that mappings can be added/removed by other
>>>> mappings and by autocommands.
>>>
>>> I do not see how this complication is relevant to the discussion. I.e.
>>> I do not see how this complication should affect usage or
>>> implementation of both proposed changes.
>>>
>>>>
>>>> Disabling and re-enabling mappings is definitely more efficient than
>>>> removing and adding-back mappings.
>>>
>>> And it is also definitely both harder to implement and less flexible.
>>
>> Harder to implement, perhaps, but not necessarily less
>> flexible. Though the discussion thus far has centered mostly
>> on enable/disable functionality, there's nothing about the map
>> handle interface that limits it to this. It could support
>> query and execute functions, for instance. For cases in which
>> you wish to keep the original behavior, but need to "wrap" it
>> somehow, you could use the map handle to attach prolog/epilog
>> callback functions to a map. Presumably, such callback
>> functions (which could be either lambdas or funcrefs) would
>> accept an argument that allowed them to obtain information
>> about the original map, possibly even its exact lhs and rhs.
>> The prolog callback would be even more useful if Vim provided
>> a way (e.g., nonzero return) for it to abort the original map.
>
> Enable, disable, query, execute plus two callbacks. *Four* functions
> and two callbacks in place of just two simple functions, mostly using
> the functionality that is already there. Five if you remember about
> :mkvimrc and that somebody may want to replace that on top of new API:
> query will need a mirror function for creating a mapping then.
>
> This is going to introduce a big amount of bugs just to add the
> flexibility which is naturally available through a much simpler
> approach. Emulating everything you mention on top of current VimL
> state plus mappings_dump()/mappings_load() / (mappings_clear()*) is
> not going to make plugins considerably slower (as long as you can
> operate on lists and use `map()`/`filter()`/etc: main VimL
> optimization principle is “the less Ex commands the faster the code”)
> and I do not see any other benefits, except for “with some handles
> implementation it may be slightly easier to pinpoint third-party
> plugins’ bugs”.
>
> * Found an issue in my proposal: `:execute 'unmap'` would not be easy
> or efficient to use, so additionally need either `mappings_clear({list
> to clear})` or make `mappings_load()` unmap mappings when rhs key is
> missing.
>
>>
>> Hmm... This may be overkill, but it might even be possible to
>> support the idea of a "virtual map handle": i.e., a handle not
>> to a specific map, but to a *set* of maps matching certain
>> criteria: e.g., <buffer>, <expr>, maps matching a mode mask,
>> maps starting with specific char(s), etc...  Once such a
>> virtual handle had been obtained, a single call would suffice
>> to enable/disable, or even attach callbacks to all maps in the
>> set. Of course, some operations (e.g., execute) would be
>> permitted only on non-virtual (single map) handles.
>
> And this is just mappings_dump() + filter() with my approach without
> any need to invent a new DSL to describe criterias (or not invent DSL,
> but use VimL expressions which would be just as efficient as
> filter()). If I got it right then plus some way to attach callbacks to
> “new mapping defined” event to keep “callback attached” state.

So if I understand your approach correctly, what I've been calling "handles"
would be simply maparg-style dicts, and it would be up to the user to filter
on the dict members using map() and lambdas/funcrefs. With the user
responsible for all filtering, the API functions would mostly just accept
lists of these handle dicts. I believe this approach would be sufficient,
though for reasons of both efficiency and convenience, it would be nice if
functions that return lists of mappings allowed the caller to limit the range
of mappings returned somehow. It could be optional args, some sort of filter
criteria dict, or even an an optional predicate lambda/funcref. The
lambda/funcref predicate would permit complete flexibility (provided the
handle dict contained keys for all relevant attributes), though it would
probably be less efficient, since Vim would be forced to apply it to every
single mapping in the system, while it could most likely optimize the other
approaches.

As for ambiguous/conflicting maps, not sure whether you were proposing to keep
some sort of maplist() function for that, or whether you would require the
user to inspect the output of mappings_dump() to determine ambiguity/conflict.
I'd definitely favor the maplist() approach, since determining
conflict/ambiguity can be a bit tricky, especially when multi-byte encodings
are involved. But of course, anything is possible with mappings_dump(),
provided it accepts a predicate lambda/funcref, which in turn receives all
relevant map attributes. I guess it really just boils down to a tradeoff
between Vim development time/effort/bugs and plugin developer convenience...

Sincerely,
Brett Stahlman

>
>>
>> Sincerely,
>> Brett Stahlman
>>
>>>
>>>>
>>>> --
>>>> BLACK KNIGHT: I'm invincible!
>>>> ARTHUR:       You're a looney.
>>>>                  "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD
>>>>
>>>>  /// Bram Moolenaar -- [hidden email] -- 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    ///

--
--
You received this message from the "vim_use" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

---
You received this message because you are subscribed to the Google Groups "vim_use" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Bug/non-determinism in output of maparg() and map commands

Bram Moolenaar
In reply to this post by Nikolay Aleksandrovich Pavlov

Nikolay Pavlov wrote:

> 2017-05-27 18:02 GMT+03:00 Brett Stahlman <[hidden email]>:
> > On Sat, May 27, 2017 at 8:35 AM, Nikolay Aleksandrovich Pavlov
> > <[hidden email]> wrote:
> >> 2017-05-27 12:45 GMT+03:00 Bram Moolenaar <[hidden email]>:
> >>>
> >>> Nikolay Pavlov wrote:
> >>>
> >>>> 2017-05-26 20:43 GMT+03:00 Bram Moolenaar <[hidden email]>:
> >>>> >
> >>>> > Brett Stahlman wrote:
> >>>> >
> >>>> >> >> On Tuesday, May 23, 2017 at 8:25:33 AM UTC-5, Brett Stahlman wrote:
> >>>> >> >> > On Tue, May 23, 2017 at 4:35 AM, Bram Moolenaar <[hidden email]> wrote:
> >>>> >> >> > >
> >>>> >> >> > > Brett Stahlman wrote:
> >>>> >> >> > >
> >>>> >> >> %--snip--%
> >>>> >> >> > >
> >>>> >> >> > > The best solution is probably to also add the raw rhs, with the terminal
> >>>> >> >> > > codes replaced.  This won't work when changing the terminal type, but
> >>>> >> >> > > that is very unlikely to happen.
> >>>> >> >> >
> >>>> >> >> > You mean adding a key such as "raw_rhs" to the dictionary returned by
> >>>> >> >> > maparg()? If so, then yes this would help, but there would still need to
> >>>> >> >> > be a way to determine lhs, which is currently even more ambiguous than
> >>>> >> >> > rhs. While it's true that I probably already have lhs if I'm calling
> >>>> >> >> > maparg(), I need a way to determine which lhs(s) is/are ambiguous with a
> >>>> >> >> > given lhs. Mapcheck() gives me only the rhs of the conflicting map. To
> >>>> >> >> > save and restore, I'd need to know the lhs in canonical form as well.
> >>>> >> >>
> >>>> >> >> Perhaps mapcheck() could take an optional arg requesting something more than a simple boolean return. When called with this extra arg, mapcheck() could return a conflicting/ambiguous lhs (or list thereof) in some canonical format (possibly determined by the value of the extra arg itself). As long as the format returned could be fed to maparg(), it would be possible to find conflicting mappings, remove them temporarily, and subsequently restore them...
> >>>> >> >
> >>>> >> > If you define a mapping you will want to know whether the mapping
> >>>> >> > already exists and needs to be restored.  For that you can use maparg(),
> >>>> >> > no need to use mapcheck().
> >>>> >> >
> >>>> >> > Not sure why you would want to remove "conflicting" mappings. Perhaps
> >>>> >> > when you map the ; key, and the user has ;x mapped?  Then you would need
> >>>> >> > a list.  Adding a maplist() function would be better than adding
> >>>> >> > arguments to mapcheck().
> >>>> >>
> >>>> >> Yes. Very much like that. I'm implementing a sort of transient mode, in
> >>>> >> which I'll "shadow" existing maps with very short (generally single
> >>>> >> character) mappings, which are expected to be ambiguous/conflicting with
> >>>> >> existing maps, and even builtin operators. Of course, when I exit the
> >>>> >> transient mode, I'd need to restore the mappings that were shadowed.
> >>>> >>
> >>>> >> The global and builtin maps are not a problem, since the transient maps use
> >>>> >> <buffer> and <nowait>; however, without parsing the output of one of the :map
> >>>> >> functions, I have no way of knowing which buf-local mappings will be ambiguous
> >>>> >> with the transient maps I'm defining. And parsing the :map output is
> >>>> >> problematic for the reasons already mentioned: e.g., no way to tell the
> >>>> >> difference between function key <F8> and the corresponding 4 characters. I'd
> >>>> >> actually considered taking some sort of iterative approach: e.g., trying all
> >>>> >> possible permutations of lhs as input to maparg() and testing the results, in
> >>>> >> an attempt to deduce the canonical form, but this would be extremely messy,
> >>>> >> and I don't even know whether it would be deterministic... The maplist()
> >>>> >> function you mentioned, if it returned all ambiguous left hand sides in
> >>>> >> canonical form, or even a list of the corresponding maparg()-style
> >>>> >> dictionaries, would be perfect. Of course, there would also need to be a way
> >>>> >> to get the rhs's canonical form: e.g., the extra "raw_rhs" key in the maparg()
> >>>> >> or maplist() dictionary.
> >>>> >
> >>>> > OK, so for this you would use maplist() to get the list of mappings to
> >>>> > disable, use maparg() to get the current mapping, clear the mapping, do
> >>>> > your stuff, then restore the cleared mappings.  You then need to make
> >>>> > sure you restore the mappings exactly as they were, even when your
> >>>> > "stuff" fails miserably.
> >>>> >
> >>>> > It's a lot easier if we would have a way to temporarily disable
> >>>> > mappings.  It's mostly the same as above, but you won't need to use
> >>>> > maparg() to get the current mapping and the restore operation.  Instead
> >>>> > you would disable instead of clear, and later re-enable instead of
> >>>> > restore.  Still need to make sure the re-enbling does happen, no change
> >>>> > in that part.
> >>>>
> >>>> Not sure I understood what exactly you suggest to disable/restore. All
> >>>> mappings at once with one command? I would actually disagree here: I
> >>>> need something similar for translit3, but it only remaps
> >>>> single-character mappings, leaving most of other user mappings alone.
> >>>> One mapping at a time? It would be good, but given that request is
> >>>> temporary remapping naming the functionality enable/disable looks
> >>>> strange. And there are still issues with determining {lhs}.
> >>>
> >>> Let's use an example: Suppose a plugin has a special mode for entering
> >>> data (e.g. chemical formulas).  It would then map some keys, e.g. "a".
> >>> If the user already has a mapping for "a" it needs to be restored when
> >>> leaving the special mode.  If the user has mappings starting with "a" we
> >>> would like to disable those, to avoid the timeout waiting for the next
> >>> character.
> >>>
> >>> We do not want to disable mappings that don't interfere, to maximise the
> >>> freedom for the user to use other mappings at the same time.
> >>>
> >>>> One of the logical variants would be `:map <push> {lhs}
> >>>> {new-rhs}`/`:unmap <push> {lhs}`+`:map <pop> {lhs}`, but this is hard
> >>>> to implement and is rather limited, though less limited then
> >>>> enable/disable everything variant.
> >>>
> >>> This quickly gets complicated if we need to take into account all the
> >>> possible modes a mapping can be used in.
> >>>
> >>>> I would instead suggest a function mappings_dump()/mappings_add():
> >>>> first is similar to `nvim[_buf]_get_keymap` and should dump all
> >>>> mappings as a list of maparg()-like dictionaries. Second should define
> >>>> mappings being given a list of them. Of course, this means that
> >>>> dictionaries need to be fixed to allow correctly saving/restoring.
> >>>>
> >>>> The advantages:
> >>>>
> >>>> 1. Easier to implement. Code for creating a maparg() dictionary is
> >>>> already there, iterating over all mappings is not a problem. Results
> >>>> needs to be incompatible with maparg() or use additional keys though:
> >>>> e.g. Neovim altered the contents of `noremap` and `buffer` keys: first
> >>>> is now 0, 1 or 2 (you can’t correctly restore a mapping if you can’t
> >>>> distinguish `map <script>` and `noremap`) and second is a buffer
> >>>> number or zero.
> >>>> 2. More flexible: you can save and restore everything, push or pop
> >>>> individual mappings, create a temporary mapping which is just like
> >>>> mapping X, but has `<Plug>(Translit3TemporaryMap)` lhs instead (to be
> >>>> returned from `<expr>` mappings in order to select either plugin
> >>>> behaviour or fall back to previously present user mapping instead).
> >>>>
> >>>>    I can imagine other usages enable/disable or push/pop could not
> >>>> achieve: generating configuration with mappings like :mkvimrc, but
> >>>> allows doing adjustments (parsing `:mkvimrc` output is not fun,
> >>>> especially if you want to be forward compatible), creating a plugin
> >>>> which analyses how often different mappings are used (need to copy all
> >>>> mappings to temporary then replace existing mappings with plugin
> >>>> ones).
> >>>> 3. This is also forward compatible: just need to state in the
> >>>> documentation that new significant keys may be added in the future to
> >>>> the dictionaries so they should be preserved.
> >>>
> >>> I don't see much use for this.  I can't think of a practical example how
> >>> a plugin manipulates mappings it didn't create itself or even knows what
> >>> they are for.
> >>
> >> Still Vim has :mkvimrc which does manipulate (dump) mappings from
> >> third-party plugins. Also I need this functionality for some <expr>
> >> mappings: if some condition is true (e.g. `>` is preceded by `-` (C,
> >> completion) or transliteration mode was enabled, or transliteration
> >> mode is enabled *and* character that does not start a new
> >> transliteration sequence is a continuation of previous one) use plugin
> >> mapping. If it is false, fall back to whatever was there previously,
> >> including falling back to whatever mapping was there previously.
> >>
> >> Also check https://github.com/neovim/neovim/issues/6123, this is the
> >> issue backing Neovim nvim_get_keymap() API function.
> >>
> >>>
> >>> Another complication is that mappings can be added/removed by other
> >>> mappings and by autocommands.
> >>
> >> I do not see how this complication is relevant to the discussion. I.e.
> >> I do not see how this complication should affect usage or
> >> implementation of both proposed changes.
> >>
> >>>
> >>> Disabling and re-enabling mappings is definitely more efficient than
> >>> removing and adding-back mappings.
> >>
> >> And it is also definitely both harder to implement and less flexible.
> >
> > Harder to implement, perhaps, but not necessarily less
> > flexible. Though the discussion thus far has centered mostly
> > on enable/disable functionality, there's nothing about the map
> > handle interface that limits it to this. It could support
> > query and execute functions, for instance. For cases in which
> > you wish to keep the original behavior, but need to "wrap" it
> > somehow, you could use the map handle to attach prolog/epilog
> > callback functions to a map. Presumably, such callback
> > functions (which could be either lambdas or funcrefs) would
> > accept an argument that allowed them to obtain information
> > about the original map, possibly even its exact lhs and rhs.
> > The prolog callback would be even more useful if Vim provided
> > a way (e.g., nonzero return) for it to abort the original map.
>
> Enable, disable, query, execute plus two callbacks. *Four* functions
> and two callbacks in place of just two simple functions, mostly using
> the functionality that is already there. Five if you remember about
> :mkvimrc and that somebody may want to replace that on top of new API:
> query will need a mirror function for creating a mapping then.

You are completely missing the point: those two functions don't provide
the functionality we are talking about here.

> This is going to introduce a big amount of bugs just to add the
> flexibility which is naturally available through a much simpler
> approach. Emulating everything you mention on top of current VimL
> state plus mappings_dump()/mappings_load() / (mappings_clear()*) is
> not going to make plugins considerably slower (as long as you can
> operate on lists and use `map()`/`filter()`/etc: main VimL
> optimization principle is “the less Ex commands the faster the code”)
> and I do not see any other benefits, except for “with some handles
> implementation it may be slightly easier to pinpoint third-party
> plugins’ bugs”.
>
> * Found an issue in my proposal: `:execute 'unmap'` would not be easy
> or efficient to use, so additionally need either `mappings_clear({list
> to clear})` or make `mappings_load()` unmap mappings when rhs key is
> missing.
>
> >
> > Hmm... This may be overkill, but it might even be possible to
> > support the idea of a "virtual map handle": i.e., a handle not
> > to a specific map, but to a *set* of maps matching certain
> > criteria: e.g., <buffer>, <expr>, maps matching a mode mask,
> > maps starting with specific char(s), etc...  Once such a
> > virtual handle had been obtained, a single call would suffice
> > to enable/disable, or even attach callbacks to all maps in the
> > set. Of course, some operations (e.g., execute) would be
> > permitted only on non-virtual (single map) handles.
>
> And this is just mappings_dump() + filter() with my approach without
> any need to invent a new DSL to describe criterias (or not invent DSL,
> but use VimL expressions which would be just as efficient as
> filter()). If I got it right then plus some way to attach callbacks to
> “new mapping defined” event to keep “callback attached” state.

--
Westheimer's Discovery:
        A couple of months in the laboratory can
        frequently save a couple of hours in the library.

 /// Bram Moolenaar -- [hidden email] -- 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    ///

--
--
You received this message from the "vim_use" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

---
You received this message because you are subscribed to the Google Groups "vim_use" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.
12
Loading...