2017-05-27 19:32 GMT+03:00 Brett Stahlman <brettstahl...@gmail.com>:
> On Sat, May 27, 2017 at 10:39 AM, Nikolay Aleksandrovich Pavlov
> <zyx....@gmail.com> wrote:
>> 2017-05-27 18:02 GMT+03:00 Brett Stahlman <brettstahl...@gmail.com>:
>>> On Sat, May 27, 2017 at 8:35 AM, Nikolay Aleksandrovich Pavlov
>>> <zyx....@gmail.com> wrote:
>>>> 2017-05-27 12:45 GMT+03:00 Bram Moolenaar <b...@moolenaar.net>:
>>>>>
>>>>> Nikolay Pavlov wrote:
>>>>>
>>>>>> 2017-05-26 20:43 GMT+03:00 Bram Moolenaar <b...@moolenaar.net>:
>>>>>> >
>>>>>> > 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 
>>>>>> >> >> > <b...@moolenaar.net> 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.

Implementing lambda/funcref is not needed because it is the same as
using filter(). Could only save a few listitem_alloc() calls which is
nothing. Should actually be *less* efficient because `filter()` will
use expressions and both lambdas and funcrefs use Ex commands (lambdas
are really normal functions with special names and the only line:
`return {expr}`).

>
> 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...

I do not see how multi-byte encodings make dealing with conflict
harder, but to filter out mappings which start with the same key
sequences all you need is a way to transform some sequences of bytes
representing `rhs` into canonical form which would be found in the
`lhs` key of `mappings_dump()` dictionaries. So filtering those out
should be as easy as three lines:

    let all_mappings = mappings_dump(basic_criteria)
    " basic_criteria allows filtering out mappings
    " for specific mode and buffer (or leave only globals).

    " Though given that main ideas for basic_criteria are
    " “do not reimplement filter” and “C implementation must
    " be simple” it may as well contain prefix selection.
    " I selected only above variants because it is all
    " Neovim supports.

    let canonical_lhs_prefix = mappings_canonicalize("\<F1>")
    let needed_mappings = filter(copy(all_mappings),
    \'v:val.lhs[:'.len(canonical_lhs_prefix).'-1] is# canonical_lhs_prefix')

Needs additional function to get canonical form for a random string though.

>
> 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 -- b...@moolenaar.net -- http://www.Moolenaar.net   
>>>>> \\\
>>>>> ///        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ 
>>>>> \\\
>>>>> \\\  an exciting new programming language -- http://www.Zimbu.org        
>>>>> ///
>>>>>  \\\            help me help AIDS victims -- http://ICCF-Holland.org    
>>>>> ///

-- 
-- 
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 vim_use+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to