AndrewRadev/switch.vim

Unite with vim-speeddating

Closed this issue · 6 comments

Any way to combine with tpope/vim-speeddating to reuse <C-a>/<C-x> ?
Likewise to execute original mapping if no definition match current word ?

Well, in order to use <c-a>/<c-x> for going back and forward between the different switches, you can set this:

let g:switch_mapping = '<c-a>'
let g:switch_reverse_mapping = '<c-x>'

Unfortunately, this will only work for list-style mappings, and there aren't that many of them in the built-ins. You can define your own, of course. If this is your major use-case, you could also consider a similar plugin, vim-cycle: https://github.com/zef/vim-cycle. That one already uses these mappings by default.

As for executing the original mapping, this does seem like a reasonable idea. I'll see what I can do about implementing it.

Main idea was in smart reusing of <C-a>/<C-x> and not simple remapping.
So I could execute mappings from another plugin when conditions for switch.vimweren't met.
Mentioned vim-cycle don't work with speeddating either, therefore it's no better alternative.
I'm looking forward for your implementation.
It seems like you already did that for splitjoin.vim (quote frome its help).

However, if no splitting/joining is possible at this point, the plugin will fall back to the default mapping.

Also, please clarify this

Unfortunately, this will only work for list-style mappings, and there aren't that many of them in the built-ins. You can define your own, of course.

I quite don't understand what you mean by list-style mappings, built-ins and you can define your own.
Do you speak about switch_custom_definitions emphasizing lists over dicts? Or about native vim mappings ?
I feel like you have tryied to discouraged me using <C-a/x> but I can't grasp the idea why and what I lose in that case.

Also, please clarify this

There are two types of switch definitions. In list form:

['one', 'two', 'three']

And in dictionary form:

{
    ':\(\k\+\)\s*=>\s*': '\1: ',
    '\<\(\k\+\): ':      ':\1 => ',
}

The first one switches "one" into "two", "two" into "three", and "three" into "one". The second one switches :some_key => into some_key:, which is something specific to ruby. This one can't be written as a list, because its keys are regexes and the values are their substitutions. In the list form, every item is both a pattern and a substitution.

The list form is actually compiled to:

{
    '\<one\>': 'two',
    '\<two\>': 'three',
    '\<three\>': 'one',
}

So, if you have the pattern \<two\> under the cursor, it is replaced with the next word in the list, three.

Now, if you execute the :SwitchReverse command, the list is actually compiled to:

{
    '\<one\>': 'three',
    '\<three\>': 'two',
    '\<two\>': 'one',
}

Which means that, if you have the pattern \<two\> under the cursor, it replaces it with the previous word in the list, one.

So you see, there's no way to "reverse" a dictionary switch definition, because there is no ordering to it. This is why there's just one main command, :Switch, and the reverse command was added later and only works for list mappings.

With vim-cycle, you only get list mappings, which means you can only replace one word with another. Switch gives you more flexibility, because you can use any pattern to decide what to replace it with. But it does mean that it's not very useful to think of "next" and "previous" replacements. There's only one direction: "If it matches this pattern, replace it with this substitution".

Did I explain that well enough?


As for the other question,

Main idea was in smart reusing of / and not simple remapping.
So I could execute mappings from another plugin when conditions for switch.vimweren't met.
Mentioned vim-cycle don't work with speeddating either, therefore it's no better alternative.

This is tricky. The reason that cycle doesn't support speeddating is likely that it's impossible to support it, short of directly hardcoding the logic specifically for speeddating.

Imagine what the logic would be for what I suggested: "If a switch succeeded, do nothing, but if no switches succeeded, call the built-in mapping". This is easy to do, the built-in can just be called with normal!. But how do I call a mapping that might have been set by another plugin?

If a plugin sets a mapping for <c-a> before I do, then the only thing I can do is take the mapping's right-hand-side with maparg(), store it for later, and define my own mapping. Okay, that's doable (although a bit complicated). But what if my plugin is loaded before that other plugin? There's nothing I can do, the plugin has to take care of it as well.

If all plugins that defined a particular mapping tried to do this, then maybe this could work (although even then, it's not clear if it's a desired property. I just discovered #41, which I'd forgotten about, and I explain there that it might not be a good idea to do this without an option at least). But most plugins don't, because it's pretty complicated and not always a good idea. Plus, it's expected that if some mapping doesn't work for you, you could just override it or call functions/commands.

Speeddating comes with <Plug> maps you could call directly: https://github.com/tpope/vim-speeddating/blob/master/plugin/speeddating.vim#L43-L48. Executing normal <Plug>SpeedDatingUp should increment a date. You could even use the autoloaded function, speeddating#increment. I could provide you with a public function you could use to check if the switch worked or not, and then you call the appropriate speeddating mapping. Does that seem reasonable to you?

Thanks, you explanation is much more detailed than I could expect :).
Lets call current situation unidirectional switching.
It's not that bad, as I can live with it and bear limitations in mind.
I suppose, reverse switching will be possible by providing symmetrical regexes in other direction, but maintanance cost of doubled rules quantity is too much. Therefore, discarded.
However, could be implemented as rarely used extension. For geeks with perfection syndrom.


Next one. For lazy-loading dein.vim has on_source and depends hooks.
Therefore I can secure the relative order of plugins loading despite which was triggered first.
And maparg() variant will work just fine. For me. Yes, for me -- so, as you justify, not good.

Your variant with public api to query applicability is nice, I rather like it.
User can construct his own chain for multi-plugin application and create mapping for it, just as planned.
I would only suggest to split api for query and for actual action -- therefore anyone could insert smth in-between those two actions, if he feels like it. Likewise:

if switch#can() | do_user() | switch#apply() |else| speeddating#increment() |en

Yes, totally nice. I'm looking forward.

Separating the query and the action seems inefficient to me -- it means that the plugin will run through all the matches to see if they work, then run through them again to "apply" one. One way would be to return the match object and have a separate function to apply it, but it seems like it would be a bit clumsy to me.

Right now, I've tweaked the API a bit and changed the switch#Switch() function to not take the definitions as an argument. It already returns 1 for success and 0 for failure (if you have a recent enough Vim version, you could use v:true and v:false, they are aliases to 1 and 0). So, your mapping would look like this:

if !switch#Switch() | speeddating#increment() | endif
if !switch#Switch({'reverse': 1}) | speeddating#decrement() | endif

This is consistent with internal Vim functions as well, like search(): when you call it, it either returns a positive number (the matched line) and moves the cursor to it, or it returns 0 and doesn't move the cursor.

If you have a reason to separate the actions, you can try to convince me. I could separate the searching from the replacement by returning a "match" object (the relevant code is here), but I can't really think of a use case in which it would be interesting, other than debugging (and if you need to debug, you can easily put echomsg calls in the actual plugin).

Alright, can't provide useful meaning for separate check/apply beside my intuition based on general experience.
Maybe, I will stumble on some example throughout the future usage.

Currently, I have created two mapping:

nnoremap <silent> <Plug>(switch+date) :<C-U>if !switch#Switch()<Bar> call speeddating#increment(v:count1) <Bar>en<CR>
nnoremap <silent> <Plug>(switch-date) :<C-U>if !switch#Switch({'reverse': 1})<Bar> call speeddating#increment(-v:count1) <Bar>en<CR>

And supplemented convetional keys:

nmap <silent><unique> <C-a> <Plug>(switch+date)
nmap <silent><unique> <C-x> <Plug>(switch-date)
xmap <silent><unique> <C-a> <Plug>SpeedDatingUp
xmap <silent><unique> <C-x> <Plug>SpeedDatingDown

Maybe, one day I will be feeling irritation when trying switch and increasing date/number somewhere instead, or the other way round applying substitution contradictory to my will. But only throughout usage these corner cases can be revealed :)