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.