zsh-abbr icon indicating copy to clipboard operation
zsh-abbr copied to clipboard

Multi-word / prefixed abbreviations

Open henrebotha opened this issue 3 years ago • 11 comments

I don't really like using import-git-aliases as it produces too many conflicts with other commands. What I think would be really fantastic is if zsh-abbr could be clever enough to expand regular Git aliases contextually, i.e. when prefixed with the command git.

$ abbr add --git fp='fetch --prune'
$ git fp<space>
# expands inline to
$ git fetch --prune

What would it take to implement this? I see this in _abbr_widget_expand:

https://github.com/olets/zsh-abbr/blob/ccac4d044cec86fbb032ff9ea056eca246ea6041/zsh-abbr.zsh#L1236-L1242

I suppose we'd need another layer here, something like _abbr_git_expansion, which takes place only if $words[0] == 'git'. ~~I tried this, but even though _abbr_git_expansion fp prints fetch --prune, typing git fp<space> doesn't expand. What am I missing?~~ Zsh arrays are 1-indexed, haha.

henrebotha avatar Dec 03 '20 12:12 henrebotha

I started conversation in the PR before seeing this ticket. Relocating conversation to here.

@olets wrote Interesting. Before you sink too much time into it, can you explain the use case?

I'm excited to see what you do but heads up this week's really busy so I may be slow to respond.

[edit: I personally drop the g whenever I can — e.g. abbr s="git status". The original idea of the import was to make it easer for people who are heavily invested in Git aliases to move to abbr, not necessarily to generate final abbreviations. My initial thought is supporting a word prefix goes against to "get rid of every avoidable keystroke" philosophy. Still… very interesting]

@henrebotha wrote No worries, whenever you have time.

The use case for me is: Just like I never want to type git when g will suffice, I want to be able to use my extensive suite of Git aliases instead of typing their full equivalents. Given a regular command alias g=git, typing gs expands to git status --short and accepts it. Of course, I could simply have a command alias gs, which even accomplishes the same thing with one fewer keystroke; but then I run into the problem of colliding with other commands. (For example, I frequently have to make use of a command gp at work, which precludes having an alias such as gp='git pull'.) Having the ability to force an abbreviation to start with a particular prefix means I can "scope" all my Git aliases under a "namespace", and then go nuts with defining any Git aliases I want without having to worry about them potentially colliding with some command that just happens to start with "g".


Here's what I'm thinking:

Really cool idea. Rather than tie it to Git, I'd like to support spaces in abbreviations generally.

It would meet the Git need abbr "git p"="git pull" and any other complex cases users might come up with, and would protect against requests like "there's support for a git prefix, can we add support for an x prefix?"

Data handling should be pretty straightfoward — qs and Qs where necessary (takes special care when updating, but it should be able to follow the existing support of multi-word expansions). In fact that part should be cleaner than supporting a Git prefix only.

The hard part is expansion, because there can be overlap.

abbr "a b a b"=d
abbr -g a=e
abbr -g b=f
abbr -g "a b"=g
abbr -g "b a"=h
abbr -g "a b a"=i
abbr -g "b a b"=j

I think longest (the one with the most spaces) should probably win — given

abbr "a b"=c
abbr -g b=d

a b<space> should become c. Is it okay that b can't expand to d if the b is preceded by the word a. Maybe? Possible there's some common use case where that behavior would be annoying. If not, could be up to the user to not block themselves.

I think hardest —and possibly even a deal breaker— is a performant implementation of the upgraded functions. But maybe it'll shake out as a pretty recursion.

//

Would you be interested in working on this?

Do you have an intuition about what the user expectation is for overlapping abbreviations?

olets avatar Dec 04 '20 22:12 olets

Really cool idea. Rather than tie it to Git, I'd like to support spaces in abbreviations generally.

This is actually something I've started working on myself as a separate utility. The idea of "scoped aliases" is so useful to me in contexts like Git and ssh, I want that behaviour everywhere! So I agree: we should try to support this for any arbitrary command.

A simple but effective solution occurs to me w.r.t. overlapping abbreviations: we simply don't allow spaces in global abbreviations, and we always prefer a spaced abbreviation over a global one. That makes conflicts straightforward to resolve, and it makes it easy for the user to understand what to avoid (i.e. don't make global abbreviations that match subwords of spaced abbreviations).

henrebotha avatar Dec 17 '20 11:12 henrebotha

don't allow spaces in global abbreviations, and we always prefer a spaced abbreviation over a global one

Perfect 👍

--

zsh associative array keys can have spaces as long as the entire key is quoted. So conceptually implementation won't be bad:

  1. _abbr_widget_expand passes _abbr_cmd_expansion the entire LBUFFER instead of just the last word
  2. the add and rename functions [edit: and erase and probably others] don't error for multiple-word abbreviations iff the abbreviation is quoted
  3. quotes are handled correctly (in temp storage file, config file, listing, expanding, etc)
    • I expect a lot of lines will have to change, but it shouldn't be too many distinct things to solve.
  4. tests cover adding, deleting, erasing, listing, and renaming [edit: anything else?] multi-word abbreviations

Won't have much spare time for a couple months. If you want to take a stab at it that'd be awesome! I'm happy to support.

olets avatar Dec 18 '20 23:12 olets

It occurs to me now that one issue with handling it this way is that we can't really have abbreviations that are substrings of other abbreviations. For example:

abbr 'y l'='yarn lerna'
abbr 'y l r'='yarn lerna run'

What should happen if the user types y l ? Do we immediately expand according to the first expansion, or do we somehow wait to discover which of the two was intended?

I suppose this only affects inline expansion with Space; expand-and-accept with Enter would still work as intended (since pressing Enter serves to disambiguate).

henrebotha avatar Dec 26 '20 16:12 henrebotha

I think immediately expand on y l. Otherwise the computation gets complex quick. Prefixed/scoped/multiword abbreviations are an advanced feature and I think it’s reasonable to ask users to work within some limits.

Can think of it as two scopes: yarn abbreviations, and yarn lerna abbreviations:

abbr “y l”=“yarn lerna”
abbr “yarn lerna r”=“yarn lerna run”

Then —provided yarn and yarn lerna aren’t abbreviations themselves— y<space>l<space>r(<space>|<enter>) becomes yarn lerna run

olets avatar Dec 27 '20 19:12 olets

@henrebotha I've pushed up a wip branch, prefixes (diff). The idea is to treat abbreviations and expansions the same — so where abbr a=b currently is saved as abbr a="b" in the config file, with this change it would be saved as abbr "a"="b", and all the other quote level manipulation would be the same as well. Bad news is it doesn't really work right now, good news is I think it's just a question of following the data around and making sure we do with the abbreviation what we do with the expansion (and maybe find a way to simplify both? but that can also come later).

I've also added placeholders for some tests we'll need, and added a new test harness (in main - diff) to make test writing a nicer experience.

Todo:

  • fix test regressions
  • write new tests

Don't know when I'll get back to this — probably not soon. If you're inspired to pick it up that'd be awesome!

olets avatar Jan 22 '21 18:01 olets

@henrebotha 🎁 I've built this out

If you're up for it I'd appreciate your trying it out! Branch is multi-word-abbreviations. (Back up your abbreviations just in case)

For you original request

% git fp<space> # expands inline to `git fetch --prune`

do

% abbr "git fp"="git fetch --prune"
% git fp<space> # expands inline to `git fetch --prune`

Taking that further, in theory you could

% abbr g=git
% abbr "git fp"="git fetch --prune"
% g<space>fp<space> # expands inline to `git fetch --prune`

olets avatar Dec 20 '21 01:12 olets

Legend! Going to try it now.

henrebotha avatar Dec 20 '21 08:12 henrebotha

Works beautifully! I'll report back if I come across any issues.

henrebotha avatar Dec 20 '21 09:12 henrebotha

I've been using this very happily over the last few months and have had no trouble with it at all. This is a game-changer! Thank you so much for making the effort of implementing this. I'd like to make a token donation to an org of your choice by way of thanks; perhaps a Hawaiian organisation championing indigenous rights? Let me know.

henrebotha avatar Mar 04 '22 11:03 henrebotha

Great to hear, and encourages me to get it released.

Amazing!! Let me think and get back to you with a couple groups to choose from.

olets avatar Mar 08 '22 22:03 olets

Time flies!

I've listed a couple organizations in the v5 branch README (see also the new v5.0.0.beta-1).

Thanks again. The sentiment is inspiring.

olets avatar Nov 13 '22 16:11 olets

@all-contributors please add @henrebotha for code, ideas, and financial. (https://github.com/olets/zsh-abbr/pull/31, https://github.com/olets/zsh-abbr/issues/32)

olets avatar Feb 24 '23 02:02 olets

@olets

I've put up a pull request to add @henrebotha! :tada:

allcontributors[bot] avatar Feb 24 '23 02:02 allcontributors[bot]