tpope/vim-speeddating

Suggestion: add support for true/false, on/off, yes/no…

denilsonsa opened this issue · 14 comments

I found this old plugin called Toggle (github mirror) and it uses an inconvenient shortcut. And there is also toggle_words (github mirror) that does basically the same thing. And then I though: it makes a lot of sense to toggle booleans using ^A and ^X!

So, as a feature request, you could add this feature to speeddating. No need to support everything, just the most common cases:

  • true/false; True/False; TRUE/FALSE (case-preserving)
  • on/off; On/Off; ON/OFF (case-preserving)
  • yes/no; Yes/No; YES/NO (case-preserving)

I'm not sure if toggling if/elseif/else and define/undef is useful.

I actually had this idea some time back. This was my attempt at it: https://gist.github.com/1290527. I didn't like all the redundancy in setting up the keywords so I never pursued merging it in. If we could find something a bit more sophisticated, I think it would be good to go.

But do we actually need anything more sophisticated? My only suggestion is to write that dict in multiple lines (instead of a long single line), with a blank line between unrelated "graph cycles". Other than that, the code seems simple enough, fast enough, and powerful enough to support things like if→elseif→else→if→… And also to support other languages if anyone wants.

But I don't know how you implement the "backwards" search. Well, for cycles of 2 items (like all boolean values), it's possible to map the decrement to the increment, and it will work correctly. For cycles of 3 or more items (if/elseif/else), it would require a "forward" dict and a "backward" dict.

Anyway, wanna avoid the redundancy? Try something like this (rough algorithm in Python, because I'm not familiar with vim-script syntax or libraries):

cycles = [
    ['true', 'false'],
    ['True', 'False'],
    …
    ['if', 'elseif', 'else'],
    …
    # I see no reason to try automatically generate uppercase/lowercase versions,
    # it's better to directly list here all the variations.
]
forward_keywords = {}

# This should be called whenever the script tries to increment/decrement a keyword.
# This could also be called at main script initialization, but it's not needed.
# Let's speed up the initialization an call this lazily, only when needed.
def init_keywords():
    if forward_keywords:
        return
    for cycle in cycles:
        prev = cycle[-1]
        for item in cycle:
            forward_keywords[prev] = item
            prev = item

"Backwards" search is completely ignored and works by coincidence when it's just pairs of keywords. That's the practical problem with the redundancy. Well that, and I want it to be simpler so that users can customize it, if only unofficially.

Your algorithm has the right idea. In fact, we can skip the whole hash thing and just iterate over the list of lists. But trying that out revealed what the real problem originally was. The regexp near the end of my gist is statically generated at plugin load time. I'd much rather allow dynamic overrides (think :SpeedDatingKeywords hi bye). None of this is unsolvable. Just complicated enough for me to forestall implementing it.

And for the record, incrementing from if to else strikes me as positively nonsensical. if lines have a conditional; else lines don't. But all the more reason to make it customizable, I suppose.

I agree that if/else thing seems nonsensical, I just mentioned because that's what toggle_words.vim also does, even though probably I won't ever use that. But I also mentioned because it was the only 3-cycle that came to my mind (even though that was a silly example). But now I thought about some others that are much more useful:

  • debug/info/warning/error/exception/critical (loglevels for logging messages, also available in UPPERCASE; these would be helpful for me as I never remember all of them)
  • highest/high/medium/low/lowest (think about the priority of a bug or a task)
  • YiB/ZiB/EiB/TiB/GiB/MiB/KiB
  • yotta/zetta/exa/peta/tera/giga/mega/kilo/hecto/deca/deci/centi/milli/micro/nano/pico/femto/atto/zepto/yocto (thanks, Wikipedia!; I'm not sure about those in italics, since they break the 1000**k pattern)
  • months of the year? days of the week? Both of them localized, but supplied by the user instead of the plugin?
  • long long/long/int/short/char (these might be tricky, maybe just long/short and int/char; or maybe these aren't useful at all)
  • long double/double/float (maybe just double/float; or maybe these aren't useful at all)
  • alfa/bravo/charlie/delta/echo/…/zulu (NATO phonetic alphabet)
  • Okay, maybe I'm stretching this list too much... :)

I'm not sure if iterating over lists is a "better" solution than storing a dictionary (or a pair of dicts, one forward and one backward). Sure, for small lists it will be fast enough; and it is somewhat unlikely that the list will get too big. Well, I believe that using a dictionary will be faster even for any arbitrary quantity of items.

About the regexp... Maybe you should use a simple static regexp like this: \w+ and then check if the matched string can be incremented/decremented. If it can't, then try the next algorithm. And let the user change that regexp if needed (by setting g:speeddating_something_regexp).

Okay, nevermind the previous regexp idea. It's not good enough. Just implement something to rebuild the regexp whenever the user wants to interactively customize the plugin.

I hope you don't mind this almost brainstorm message. There won't be many of these, anyway. :)

Do you know about the swapit plugin? It has similar functionality, and I have sent a patch to the author implementing a fallback to speeddating when none of the swaps apply. This works quite well for me; check it out if you're still interested!

Hey guys, I'm dealing with a horrifying code base and the DB is littered with weekday strings (monday, tuesday, wednesday columns everywhere), and I figured in addition to binding Ctrl+A/X to date strings and toggleable word-pairs, word groups such as the sets of week-of-day strings and month strings can be added to this as well!

Edit: Oh wow, @denilsonsa has already mentioned this. Yep, we just need to be able to define these lists in a localized way.

FWIW day of week already works with :SpeedDatingFormat %A.

Awesome, this does work (but only on capitalized full days of the week -- I'll play with it to see if I can get it to work on 3-char versions and also their lowercase brethren). What is the right way to configure this? Should I call :SpeedDatingFormat %A in my vimrc?

You'll need to do it in after/plugin/speeddating.vim, or a VimEnter autocommand.

okay that makes a lot of sense, thanks

@denilsonsa Have you tried implementing the lists you mentioned using SwapIt? Either way, integrating such a system with a hashing datastructure for speeddating will be total win (if not feature-creeping speeddating), but I do see a real challenge in how to do the regex matching.

Sorry, I haven't tried to implement it.

On Fri, Mar 13, 2015 at 12:39 PM, Steven Lu notifications@github.com
wrote:

@denilsonsa https://github.com/denilsonsa Have you tried implementing
the lists you mentioned using SwapIt? Either way, integrating such a system
with a hashing datastructure will be total win, but I do see a real
challenge in how to do the regex matching.


Reply to this email directly or view it on GitHub
#4 (comment).

Denilson Figueiredo de Sá
http://denilson.sa.nom.br/

I just tried out swapit, as I like the functionality asked for by the OP. For what it's worth, swapit does "true" and "True" (for example), but not "TRUE". Maybe knowing that makes the OP's request more manageable, assuming that same behavior is desired.

+1 @tpope
this looks great.