Abbreviations #731

Closed
dag opened this Issue May 11, 2013 · 91 comments

Projects

None yet
@dag
Contributor
dag commented May 11, 2013

Introduce abbreviations as an alternative to aliases, possibly replacing them.

Abbreviations are exactly like aliases, except they're expanded into their full form live as you type them on the commandline. Say you have gc as an abbreviation for git commit; now if you type gc -am "did stuff" the commandline changes when you type the space after "gc" and finally ends up as git commit -am "did stuff". This should only happen in command position, and when you type a space or hit enter.

Original text follows:

Replace aliasing with self-expanding short forms

I don't know what to call these but anyway. The idea is that if you type "g" as a command it expands to "git" when you type a space or enter.

Arguments for:

  • It solves many of the issues with completions. Since the alias expands "live", the git completions work as normal and commandline doesn't have to lie or do any other hacks like that.
  • It's easy to implement. I can kinda do it with bind and commandline although I didn't try to write a complete implementation. We don't need any changes to function or complete etc, as in other proposed solutions.
  • It's arguably "fishy": the user can see the expanded form, it's not hidden in an alias; it's instant and live, similar to completions, prompt repainting with prevd-or-backward-word etc.
  • When copy-pasting a command-line for instructions to others, it won't be full of your custom aliases that they have no knowledge of.
  • You can edit the expanded form if it's only almost-right.

Arguments against:

  • Doesn't solve the problem with alias git=hub, a problem that doesn't currently exist but is introduced with some alias completion proposals such as this one. However you can simply write a normal function function git; hub $argv; end instead to side-step this proposed alias system.
  • Some might find it surprising to have the command-line change as you type, without pressing anything like Tab or such.
  • The reason you can edit the expanded form is also the reason you can't delete what you typed with simply two backspaces. Even CTRL-W might not be enough as you might have gc expand to git commit for example. CTRL-U will do if it's the only command but it will delete too much if you're using the alias for a pipe for example. Perhaps we could introduce a new binding for "kill current command but not whole buffer".

Prior art:

  • :abbreviate in Vim
  • I think maybe search engines in Chrome?

Discuss.

@dag
Contributor
dag commented May 11, 2013

Another idea is to name this "abbreviations" like in Vim, and then decide what to do with aliases in another issue [I tend to think they should be removed].

Thought of another argument against:

  • Can't use these "aliases" in scripts. Not sure it's a good idea to use aliases in scripts anyway, but we could have a function for expanding "abbreviations" and/or we could hook fish's abbreviations into editors to have them expand on input there as well.
@terlar
Contributor
terlar commented May 11, 2013

I like your proposal, sounds cool and fishy 👍

@gustafj
gustafj commented May 12, 2013

I also like this.
It's a very clean solution to the "completions for aliases" #393 problem which is quite annoying.
This combined with a ll (Alias) printout when doing a ll<tab> would be perfect.

As alias is just a script wrapper around function today, I would want that changed as well so that there is a distinct difference between functions and aliases since they differs in so many ways after this, ex:

  • Syntax
  • Expressibility
  • Usage
@dag
Contributor
dag commented May 12, 2013

@gustafj I think it's probably better to call this idea "abbreviations" and replace alias with something that shows a helpful error message pointing at function and abbreviate. Thoughts?

@dag
Contributor
dag commented May 14, 2013

Another note:

I meant for this to only affect the command in a commandline, because it would be really annoying if g kept expanding to git in argument position. However, we might want some expansion in argument positions as well, for example with sudo (which would mean you could use sudo with abbreviations - something you can't do with aliases!) and certain cases of unambiguous completions such as for git's own aliases or if you type part of a long option. Getting this right might be tricky though, and would only get in the way if the completions are out of date, and if you actually wanted to sudo g not sudo git you now have to do something like sudo (echo git). Perhaps we could say that quoted arguments never expand, so you could then say sudo "g". Not sure whether that would be too magical and surprising or in fact exactly what one would expect and intuit. Thoughts? :-)

@gustafj
gustafj commented May 14, 2013

Abbreviations sounds good to me (but I'm a regular vim user, so I might be biased ...).
When I think about it there are quite many "interesting" things that can be done with this, but (as you already point out) there are traps hidden in this minefield as well :/

I think it might be problematic/annoying if it is completely automatic and in any place in the commandline, mostly since undoing the abbreviation could be tedious if it expands to something you don't want and particularly if it expands to a long string.

A few thoughts of my own:
Should it be automatic by using space or should it be triggered by an option via tab, or something else?
Should it only be for simple strings ex g for git?
Should it only be applicable for the first item on the commandline, or anywhere?
Should it be possible to do string -> value conversions ex d for current date Tue May 14 20:23:03 CEST 2013?
Should it be possible to expand subshells ex vim (find . -iname '*cfg')<tab> gives

foo.cfg bar/poo.cfg foo/bar/more/beer.cfg
> vim
@dag
Contributor
dag commented May 14, 2013

Thoughts on your thoughts!

Should it be automatic by using space or should it be triggered by an option via tab, or something else?

I don't think this works with anything but space (and enter) because the whole point is to get the seamless experience you get with aliases where you don't have to think about something being an alias or not, and tab doesn't work either because g<Tab> should complete commands that begin with a g. If we add a new binding say CTRL-X we now have three types of completions: normal ones, autosuggestions and abbreviations. I really think this needs to be done with space or not at all.

Should it only be for simple strings ex g for git?

In command position it should be possible to include arguments in an abbreviation. For example gc expanding to git commit --all or what have you. Basically I think of abbreviations as being exactly like aliases, except they expand live on the command line.

Should it only be applicable for the first item on the commandline, or anywhere?

Only in command position is probably a good start. Then we can maybe experiment with the argument position use cases I suggested before.

Should it be possible to do string -> value conversions ex d for current date Tue May 14 20:23:03 CEST 2013?

You mean in argument position? echo d expanding to echo the date? I really think if we do argument position abbreviations at all, it needs to be used very conservatively and only when either the argument is a command (as in the case of sudo) or when the command has its own alias system (as in the case of git) and expanding would have the exact same effect as not expanding (so we only expand for usability).

Should it be possible to expand subshells ex vim (find . -iname '*cfg')<tab>

I think that's a question for completions and not abbreviations, as I don't think of abbreviations as involving tab and expanding subshells on space is probably a bad idea. However not sure it can work for completions either because you can already complete variable names without expanding them.

It could be useful though. Like I imagine doing rm *.png, expanding the wildcard and removing some of the files from the list.

How about this: we have a generic "expansion" or "abbreviation" system similar to the completions system; we bind CTRL-X or something for expanding tokens; and in command position we also expand abbreviations automatically. That could also make the sudo and git use cases work less obtrusively by making those expansions explicit.

It's a much bigger proposal than my original idea, though. 😉

@gustafj
gustafj commented May 15, 2013

Space as trigger sounds good to me, probably gives the best user experience.
Expanding *.png sounds interesting ;)
I myself don't like a lot of different bindings for different things, perhaps "double tab" could be used instead of a new binding.
Ex ....*.png<tab> gives press <tab> again to expand *.png (the same for subshells).

But it might be better to limit this proposal to: abbreviations for the command position, expanding using space.
And have "expand subshells/wildcards" & sudo support as a separate one?

@dag
Contributor
dag commented May 15, 2013

Not sure double tab would work as that currently cycles between completions. Vim seems to have CTRL+] for expanding abbreviations without typing a space...

I think if we do live expansions at all, then abbreviations should be implemented with that somehow, so the ideas are related. But if we don't do live expansions, abbreviations should still be considered on their own. So not sure what to do here. :-)

@gustafj
gustafj commented May 16, 2013

Yeah your right about <tab>, was only thinking for subshells (where it currently does nothing).
It could be possible to change the current behavior of <anything><space><tab> which currently cycles through all available files in the current dir (first printing them all).
Ex

> ls <tab>
a.cfg  b.cfg  c.cfg  d.cfg
> ls <tab>
> ls a.cfg<tab>
> ls b.cfg<tab>

But i would rather have "live expansions" using a new key-combo than living without it ;)
Expanding *.cfg to a.cfg b.cfg c.cfg d.cfg feels really useful.
But then all forms of possible expansions should be expandable, not just wildcards, ex brace completion (which i would rather see removed ... but that is a different issue #354)

@dag
Contributor
dag commented May 16, 2013

But then all forms of possible expansions should be expandable, not just wildcards, ex brace completion (which i would rather see removed ... but that is a different issue #354)

Agreed on both accounts. I wonder if double quotes should be expandable too... echo "hello $USER" -> echo "hello dag".

@dag
Contributor
dag commented May 16, 2013

I think what we're talking about here is actually "evaluation" more than "expanding" while on the other hand abbreviations are not about evaluation, so these ideas are probably separate and I'll make a new issue for the other one and edit this one a bit.

@dag
Contributor
dag commented May 19, 2013

Here's a basic working prototype:

function __fish_expand_abbreviation
    if test (count (commandline -poc)) -eq 0
        switch (commandline -t)
            case g
                commandline -t git
            case gc
                commandline -t 'git commit'
        end
    end
end

bind \  '__fish_expand_abbreviation; commandline -i " "'
bind \n '__fish_expand_abbreviation; commandline -f execute'

One problem I noticed is that if you hit enter to execute an abbreviated commandline, the expanded form is highlighted as an error (unknown command), even including the arguments.

@terlar
Contributor
terlar commented May 19, 2013

I liked this so much, so I implemented a way to manage these abbreviations.

In the end I think these should be handled the same way completions and functions are handled. With it's own abbreviations folder where it can be saved.

This is the current implementation:

function __fish_expand_abbreviation
  if test (count (commandline -poc)) -eq 0
    set -l token (commandline -t)

    if abbreviations -q $token
      commandline -t (abbreviations $token)
    end
  end
end
function abbreviations --description 'List, show and query abbreviations'
  if test (count $argv) = 0
    printf '%s\n' $fish_abbreviations
    return
  end

  set -l abbreviation_index 0
  set -l expanded_abbreviation

  for i in $fish_abbreviations
    set abbreviation_index (math $abbreviation_index + 1)
    echo $i | read -l abbreviation command

    if test $abbreviation = $argv[-1]
      set expanded_abbreviation $command
      break
    end
  end

  if test -n "$expanded_abbreviation"
    switch $argv[1]
      case -q --query
        return 0
      case -e --erase
        set -e fish_abbreviations[$abbreviation_index]
      case '*'
        echo $expanded_abbreviation
      end
  else
    return 1
  end
end
function abbreviate --description 'Define a new abbreviation'
  if test (count $argv) -lt 2
    echo 'abbreviate: Takes two arguments. First abbreviation and then expanded command'
    return 1
  end

  echo $argv | read -l abbreviation command

  eval "function $abbreviation; $command \$argv; end"
  abbreviations -e $abbreviation

  set -U fish_abbreviations $fish_abbreviations "$argv"
  return 0
end

Then you do something like this:

abbreviate !    'sudo'
abbreviate tf   'tail -f'
abbreviate l    'ls -la'
abbreviate l.   'ls -d .*'
abbreviate g    'git'
abbreviate gs   'git status'

Update 1: Add ability to remove abbreviations and store them in a universal variable.
Update 2: Create functions for abbreviations also

@terlar
Contributor
terlar commented May 19, 2013

Looking at how functions work, we could introduce:

  • abbred
  • abbrsave

Which would basically be to clone those functions.

@dag
Contributor
dag commented May 20, 2013

Nice! I'm not quite convinced we need a ~/.config/fish/abbreviations directory and the accompanying abbr{ed,save}, though. Unlike functions and completions, they're not really slow to load individually and they only need a single line to be defined. I think they're more like bind, perhaps we could have a fish_[user_]abbreviations function like the fish_user_key_bindings function, that loads the first time fish tries to expand an abbreviation? ([user_] for consistency but I'm not sure we should ship any default abbreviations?)

Only reason I can think of for having multiple files for abbreviations is if you have say an abbreviation for every git command in existence and some with arguments, so you want those separate from any others. But that's not how lazy-loading functions and completions work: they're loaded based on the command name, so if we did that for abbreviations we'd end up with one file for each abbreviation, each file containing only a single line of code!

Another idea is to do something like the universal variables, perhaps even by actually using universals (like set -U fish_abbreviations). Perhaps there is a use case for host-local abbreviations? Not sure.

@terlar
Contributor
terlar commented May 20, 2013

You have some good points and I'm leaning towards the fish_user_abbreviations way.

The other approach would be to have like completions where you put all related abbreviations into a file, but that might be harder to map.

I was planning to use set -U fish_abbreviations at first, but I like to have the config maintained by git, so prefer setting them somewhere, like a config. But the nice thing with a universal variable would be that you can add completions on the fly without any hazle, like (abbred, abbrsave). Just do a abbreviate.

One solution would be to let the command make sure abbreviations are uniquely added and overwrites existing ones. Then it would be bootstrapped with the fish_user_abbreviations and users can add custom ones to the universal variable if they want.

I have this right now:
set -q fish_abbreviations ; or fish_user_abbreviations

But that won't work if I keep maintaining the file.

@terlar
Contributor
terlar commented May 20, 2013

Updated my previous functions to use universal variable and added possibility to erase them and also check before adding new ones if they exist.

I was thinking about making it more interactive, like asking if it should replace the current abbreviation. But since it might also be scripted, I didn't want to do that. I will look into the determination of interactive mode.

@dag
Contributor
dag commented May 20, 2013

Well see thing with completions is that even they're loaded for a single command. If the current command line process is git then completions/git.fish is loaded. When you have separate command names, you have to make separate files, see for example the completions for [ef]grep which are actually in a function that's called from each of those three completion files.

With abbreviations I don't see any way to know from the abbreviation which file to load, unless the file name is the abbreviation. And then you can't have multiple abbreviations in one file. I guess we could load all abbreviation files on start, or on first attempt to expand abbreviations, but that's different from how functions and completions are loaded, and could potentially be slow if you have many abbreviation files.

New idea: abbreviate -U to make a universal abbreviation, and abbreviate -g or just abbreviate to make a global one? If we do that, perhaps we should consider doing it for bind too...

@dag
Contributor
dag commented May 20, 2013

Also I think abbreviate should just overwrite any existing abbreviation, same as bind and function and set...

@terlar
Contributor
terlar commented May 20, 2013

All are valid points. I think it makes sense to just overwrite like all other functions do.

I also like the suggestion with universal and global, which gives flexibility to choose how to handle your abbreviations/bindings. I'm not entirely sure how to implement that though, and I'm thinking maybe this should be a builtin to enhance the performance?

@dag
Contributor
dag commented May 20, 2013

Yeah, I was only prototyping it in fish scripting for trying it out. Probably don't want slow scripting to fire every time you type a space. 😉

@terlar
Contributor
terlar commented May 20, 2013

Yeah, still working great. I have been using it for about 1 day now and cannot imagine myself live without it anymore, haha 😁

@dag
Contributor
dag commented May 20, 2013

😊

Another problem I've noticed though is that if you do C-a on a non-empty commandline and type an abbreviation, it's not expanded on space since you're in the middle of a word. So say you do make install oops need root C-a ! now you have ![cursor]make install and typing the space doesn't expand the ! abbreviation. I'm gonna try to work around this with commandline -C.

A third problem, which might be a feature, is that it's only expanded when you type a space or enter after an abbreviation. So not for example if you type a space then C-a ! C-e. This could be a feature because it makes it possible to avoid abbreviation expansion, but I think it's a bit unfishy and we should instead have like a command to run a command line literally, bit like the command command but also accounting for shell functions and builtins. Or some special syntax, like \g in command position is interpreted as g and not expanded. Special syntax isn't really fishy either, but we already have space before the command special cased to mean "don't log in history", so shrug.

Fourth, and this one is an obscure one, if you do like in the previous paragraph so you have an unexpanded abbreviation followed by a space, then do C-a and space, the abbreviation is expanded and an extra space is inserted after it! Again, may need to check cursor position with commandline -C.

Oh and, the first problem I mentioned in a previous comment, it only happens if the unexpanded abbreviation is an unknown command. So a gc abbreviation expands to git commit and the whole thing highlights as an error, on hitting enter (without a space), but a gs abbreviation expands to git status and highlights correctly, because apparently I have ghostscript installed:

> which gs
/usr/bin/gs

I'm also wondering if maybe abbreviations should highlight as known "commands" even before you hit space... Currently if I type g it's red until type a space, but if I type git it highlights as known even before a space.

All these problems could probably be dealt with better if we do this in the C++ parts.

@dag
Contributor
dag commented May 20, 2013

Silly workaround for syntax highlighting problems: make an alias/function for every abbreviation. 😆

Actually, maybe that's a good idea to do anyway, hm... Means abbreviations will work in scripts, in eval, with type... Not sure.

@terlar
Contributor
terlar commented May 27, 2013

Your workaround is what I am using currently, but I'm not sure if that is what it should do in the end, or if they should be intended to be used inside eval or type. For me they should be an input thing only. But I guess it might not hurt to also let them be run to get easy support for say sudo.

So I have been using the current implementation for a while now and these are the problems discovered with this solution so far (they are not related to the approach 👍):

  1. When pasting text, the last space gets inserted at the beginning of the line, e.g. echo this is a test becomes _echo this is atest when pasted, where _ is a space.
  2. When using read builtin the bindings on enter and space applies there also. So for example if I want to input some non command to read it will expand. I guess in the final implementation this should be off by default and enabled by read -s
  3. It is a little bit slow (~0.25-0.5 seconds)
@dag
Contributor
dag commented May 27, 2013

You can work around 1. by pasting with C-y.

@terlar
Contributor
terlar commented May 27, 2013

For me C-y only pastes within fish (from what is cut inside fish) and not from my system clipboard. Copying between terminals or other apps.

@ridiculousfish
Member

I think abbreviations are a really cool idea.

@ridiculousfish
Member

I want to enable people to experiment with abbreviations to refine what the right behaviors should be. I checked in initial support for abbreviations into master as 92099c7 and f9c2a77.

An example of how to use it:

set -U fish_user_abbreviations 'gc=git checkout'
gc

gc will be expanded to "git checkout" on space or return.

Abbreviations are a list, so to add a new one:

set fish_user_abbreviations $fish_user_abbreviations 'grh=git reset --hard'

Abbreviations expand in "command positions" (for example, in subshells or as arguments to if), but not as arguments. Abbreviations do not expand in scripts either.

Here are some unresolved issues for discussion:

  1. "Abbreviation" is long and hard to spell - can we find a better name? "Alias" would be good if it weren't already used for other things. One possibility is to invert it, e.g. "expansion." fish_user_expansions sounds OK to me.

  2. How should abbreviations be specified? The gc=git checkout syntax is lightweight but isn't used anywhere else in fish.

  3. We may want to not expand on enter, only space. Expanding on enter has the disadvantage that the user doesn't see what command they ran until after they've committed to running it. If we expand only on space, then users could type the abbreviation, followed by space, followed by enter, which doesn't seem too burdensome. A second possibility is to make the first enter trigger expansion, and the second enter actually execute it.

    On the other hand, if all abbreviations are specified by the user, then the chance of the user running something accidentally is low, so expanding on enter may be fine.

  4. The expanded form of an abbreviation is just a textual substitution, and does not have to itself be a valid command. For example, you can make an abbreviation that results in mismatched quotes or parenthesis. I think this is a potentially useful feature, but it means that fish cannot do syntax checking on code containing abbreviations, which will mean we may never be able to use them in scripts.

  5. Should the abbreviation expansion itself undergo subshell and variable expansion before substitution? I think yes, because that would make them immensely flexible. Abbreviations could then run arbitrary fish code.

  6. If I hit space, and the abbreviation expands and I changed my mind, maybe I should be able to hit delete and unexpand it.

Hopefully after living on them for a while, we'll know what feels right.

@terlar
Contributor
terlar commented Jul 20, 2013

Thanks, I am running it now and it seems to work really well so far.

  1. We could use the abbreviation abbr for short. As for alias, if we think that is better, maybe it could be replaced with this behavior. If people want to use aliases in their scripts they could just do the function wrapping. Abbreviations seems to me exactly what people are using aliases for normally.
  2. I think it would be nice to have some helper functions to define and also remove abbreviations.
    For example: abbr gc 'git checkout' and abbr -e gc.
    It is a little bit cumbersome to do this manipulation through the variable in my opinion. Also it would be nice to assure that the abbreviations are unique. So you only have to define a new one to update it.
  3. I prefer the behavior that is now, many of my abbreviations like gs for git status would not be so useful if you would have to press space. However, I would be okay with double enter to actually execute it as you mentioned.
  4. Personally I'm fine with not using them in scripts.
  5. Yes, this would be cool. Cannot think of any abbreviation to use that on top of my head though.
  6. This is also a good idea, if I pressed space too early for an abbreviation for example. This would also go well together with the number 3 suggestion as double enter (still not sure if I want that extra enter though).
@xfix
Member
xfix commented Jul 20, 2013

alias is POSIX compatibility wrapper anyway. I don't have any problem with it doing abbreviations. If you write abbr gc 'git checkout' or something like that, you want completions for git checkout anyway.

@andreaseger
Contributor

i used the script implementation from terlar for the last few weeks and switched to the new native version today so here are my two cents on the open points

  1. I think it would be a great idea to just letting alias do abbreviations now. As Bonus it would fix the not working completions on current aliases.
  2. what terlar said sounds good
  3. It would really bug me if I would need to double press enter on abbreviations to execute them. It would be kind of OK if they also work unexpanded like aliases with a single press of enter but then I have unexpanded abbreviations in my history
  4. --
  5. wouldn't variable expansion mean the content of the variables will be put into the command and therefor into your history? This would make the re-usability of such commands from history way lower as they no longer contain the variable but only their earlier content. Or did I misunderstand this point?
  6. I don't see myself using the unexpand feature as I don't use abbreviation longer than 3 chars which means they are all basically in muscle memory and it would be faster to just clear the current line. This might be different now seeing that the native implementation can be used anywhere and not only on the beginning of the line. But probably a good idea if it does not mean I have to always press space or double press enter to expand them.
@dag
Contributor
dag commented Jul 20, 2013

"Abbreviation" is long and hard to spell - can we find a better name? "Alias" would be good if it weren't already used for other things. One possibility is to invert it, e.g. "expansion." fish_user_expansions sounds OK to me.

Agreed, but not sold on "expansion".

How should abbreviations be specified? The gc=git checkout syntax is lightweight but isn't used anywhere else in fish.

It should simply be a new builtin, just like complete and bind or for that matter function that the user can call but whose underlying storage is opaque.

We may want to not expand on enter, only space. Expanding on enter has the disadvantage that the user doesn't see what command they ran until after they've committed to running it. If we expand only on space, then users could type the abbreviation, followed by space, followed by enter, which doesn't seem too burdensome. A second possibility is to make the first enter trigger expansion, and the second enter actually execute it.

I really think we want to expand on Enter. I imagine I'd get a lot of frustrating fish: Unknown command “gc” and I don't imagine I'd be alone in that. However, the expansion should happen on the command line, before it is executed, such that you will see the expansion in scrollback and it is the expanded form that ends up in the history.

Should the abbreviation expansion itself undergo subshell and variable expansion before substitution? I think yes, because that would make them immensely flexible. Abbreviations could then run arbitrary fish code.

I think it shouldn't. It would usually be preferable to have those expand at the time the command line is executed. Say I have an abbreviation for creating time-stamped notes. I type the abbreviation and a space, and it expands to something like vim 12:34:56.txt. Now, I don't run this command for a few seconds, and the time-stamp will be wrong. It would be better if the abbreviation expanded to vim (time +%T).txt. This does the right thing, and also means I have the option to edit the command line before executing it, for example to use a different time format. It also makes it repeatable; for example kill %firefox can be recalled from history and re-run even when the PID has changed. Lastly, I would argue that it's more consistent with the rest of fish, where the tokens on the command line are only evaluated at execution time.

On the other hand, evaluating at expansion time is strictly more powerful because we can opt out of it with for example escapes, but without it we can't really opt in. I think this would be better solved by #751 though, as it puts the user in control of evaluation. That still doesn't provide a way to get the exact moment of abbreviation expansion though; it would be the moment of the user expanding the individual token. Still, I do think it would be more useful and more consistent, as it would work not just with abbreviations but with any token on the commandline that can be evaluated, however it was added to the commandline.

If I hit space, and the abbreviation expands and I changed my mind, maybe I should be able to hit delete and unexpand it.

I think this should not be on delete or backspace, because part of my motivation for abbreviations is the ability to post-edit parts of the expansion before execution. Perhaps a better idea would be to add a binding for "delete backwards from cursor to current process" as defined by commandline -p. This would solve the problem with abbreviations (since they only expand in command position) but at the same time be really useful without abbreviations. This would work basically like Ctrl-U but stop at the start of the current "process". A variant corresponding to Ctrl-K that stops at the end of the current process could be added for consistency and might also be useful.

@dag
Contributor
dag commented Jul 20, 2013

This might be different now seeing that the native implementation can be used anywhere and not only on the beginning of the line.

I haven't tried @terlar's implementation but it is based on mine where expansions actually work properly in any command position, not just the start of line, kudos commandline -p. I'm actually in awe at how well my crude proof of concept code works! 🐟 ❤️

@andreaseger
Contributor

hmm guess I never actually tried using abbreviations in other command positions.
Yeah the prototypes worked great only inconvenience I had was the mentioned pasting issue, but the feature was to good not to use it.

@dag
Contributor
dag commented Jul 20, 2013

@sch1zo Pasting should work if you use Ctrl-Y.

@andreaseger
Contributor

that might be the case but most of the time I copy/pasted via selecting/middle click and there the issue was present. But the native implementation does not have that problem anymore and I already updated all my machines to use that one.

@andreaseger
Contributor

So after ~10 days of using the native abbreviations I have found one thing that kind of bugs me. If you already have a command typed and then jump to the begin of the line to prepend it with an abbreviate it does not expand and will not work in its unexpanded state. A common situation for me is the following:
One of my abbreviates is !=sudo so now when I want to edit some system config file as in vim /path/to/some/file/I/am/not/allowed/to/write I often realize late that I need sudo so I typically just jump to the beginning and prepend ! unfortunately that will not expand the abbreviate and leave me with a command not found message.
My current workaround for this is to just add an alias for the abbreviate but of cause it would be nicer if it would expand properly.

@dag
Contributor
dag commented Jul 29, 2013

Agreed; we need to look at the cursor position when space is entered, not simply "the first token". Alternatively, first insert the space and then reparse the command line and expand the abbreviation.

@ridiculousfish
Member

Commit b6f495d should fix the issue that @sch1zo identified (with ! for sudo)

@Undeterminant
Contributor

Another thing to add to this: we should probably make ll, la, and the like aliases by default instead of functions.

@zanchey
Member
zanchey commented Oct 2, 2013

I'm retargeting this to next-minor as this feature will probably ship with the next release.

@ridiculousfish
Member

The next release is likely to be very soon, i.e. by the end of next week. I am just cleaning up the documentation at the moment.

Abbreviations will be included in their current, incomplete state. I don't want to publicize or document them until we have the abbrev command (or repurpose alias). For that reason I am pushing this back to next-major.

@ByScripts
Contributor

I don't think alias is a good term. For me, an alias is an alternative name... not something to be expanded.

I don't like abbr either, for the same reason.

I thought about expand foo "foobar" ... but it seems that expand command already exists :/ Too bad, I think that expand would have been the best term.

@dag
Contributor
dag commented Jan 6, 2014

How about unifying aliases and abbreviations? Instead of introducing a new function or deprecating alias, introduce a configuration option to "expand aliases". Downside here is we don't like configuration options in fish (but we could possibly skip the configuration option) and (maybe a good thing) it means we can't mix the two behaviors although you can still write normal functions to get the old alias behavior... The upside is that we don't have two ways to do similar things, it's easy to explain ("aliases expand in fish") and abbreviations will actually work in scripts (because they're also aliases) which also means they don't have to be special-cased in the syntax highlighter either. And by moving the alias machinery to the C++ code we can avoid the slowness of the current alias implementation. Still need to figure out how to make completions work for un-expanded aliases though.

@binaryphile

One feature request...

I previously was using functions to issue frequently-used command combinations in a single command, such as git add, git commit and git push in one operation.

I love the new abbreviation feature. I think it's huge and am trying to switch over to it. I'm ok with the fact that it's limited to a single command expansion. However, I still want to issue multiple commands in one go. Now I can just use an abbreviation for the first command, followed by a semicolon, then the abbreviation for the second command, etc. It works fine, in fact I think it's better this way.

My request is that the expansion be triggered not only by space or enter but also by semicolon. Since semicolon is basically equivalent to a delayed enter, I'm guessing this will make sense to you all as well.

Can't wait for this feature to drop in an official release!

@ridiculousfish
Member

Makes sense to me.

@ridiculousfish
Member

1a7b33e expands abbreviations on semicolons.

@binaryphile

Thanks!

@ElijahLynn

Where are fish_user_abbreviations stored? I want to add them to my dotfiles repo.

@ridiculousfish
Member

It's currently a universal variable, so it's stored in ~/.config/fish/config.fish. But this is just a temporary hack until we figure out the proper way to store them. Feedback here is welcome.

If you like, you can just add a line to config.fish like set -g fish_user_abbreviations... to set it manually.

@ElijahLynn

Hrm, I don't see that variable in there.

I used set -U fish_user_abbreviations 'g=git'. Is that the right way to get it into config.fish?

@ridiculousfish
Member

Sorry, I meant it would be stored in ~/.config/fish/fishd.[mac_address]. Obviously syncing that won't work (by design) since it's keyed off the MAC.

To put it in config.fish, you would write `set -g fish_user_abbreviations 'g=git'. -g for global, since you're setting it every time.

@zanchey
Member
zanchey commented Sep 20, 2014

I think the universal variable is reasonably neat, it just needs a better UI wrapped around it.

If we use it as an array, either with = to split keys and values, or ASCII FS (\x1c), then a fish function to manage it (abbr) +/- a web interface page would be useful.

Thought would have to be given to producing appropriate output for export/transfer.

@ElijahLynn

@ridiculousfish Ahh, thanks, that is very helpful! I will do that for now until there is a process for exporting.

Btw, I am neutral to having either A) a single file with all abbreviations or B) a separate file for each abbreviation. Maybe leaning towards A but not strongly.

@zanchey zanchey added a commit to zanchey/fish-shell that referenced this issue Oct 5, 2014
@zanchey zanchey abbr.fish: add abbr, a command to manipulate abbreviations
Work on #731.
642c510
@zanchey zanchey added a commit to zanchey/fish-shell that referenced this issue Oct 5, 2014
@zanchey zanchey web_config: add support for viewing abbreviations
Add a new tab which lists the current abbreviations defined, by wrapping
the `abbr` command.

Work on #731.
7af579a
@zanchey zanchey added a commit that referenced this issue Oct 5, 2014
@zanchey zanchey web_config: add support for viewing abbreviations
Add a new tab which lists the current abbreviations defined, by wrapping
the `abbr` command.

Work on #731.
7764a1a
@zanchey
Member
zanchey commented Oct 6, 2014

I added a basic UI around the existing implementation as abbr - use abbr -h to see more. I also added a view-only UI to the web interface, but it would be good to have that do more.

@zanchey
Member
zanchey commented Oct 6, 2014

Instead of using = as a separator, we could just use the first token (space-separated?) of each array item - this would simplify the implementation.

@zanchey
Member
zanchey commented Oct 6, 2014

The universal variable is neat, but if we're not exposing it directly it might be better to move it to __fish_user_abbreviations (and possibly warn on setting it at a local or global level).

@terlar
Contributor
terlar commented Oct 7, 2014

I would prefer space-separated instead of using = as separator. Especially since that style is not used anywhere else inside fish.

@kballard
Contributor
kballard commented Oct 8, 2014

I agree that this should be space-separated. It makes a bit more sense, and it means = could actually be part of the abbreviation if desired. The abbr command should probably be modified to drop the --add flag and just take two arguments instead.

Regarding $__fish_user_abbreviations, I can sympathize with this suggestion, but it is plausible that users may want to modify $fish_user_abbreviations themselves without using the abbr command, and I'd rather not encourage anyone to directly modify a $__fish_* variable. Similarly, setting it on the local or global level is perfectly legitimate; not everyone wants to use universal variables for stuff like this.

On a related note, read should probably expand abbreviations on interactive input when using the --shell flag.

@zanchey
Member
zanchey commented Oct 8, 2014

My concern with encouraging modification of the variable outside of the abbr command is that extending the command to support scoping increases its complexity in terms of UI, but not doing so leads to potentially misleading results. There is no merging of the variable scopes at present, which might be confusing - if you set a universal expansion and a (different) global expansion, should they both work? How do you unset a universal expansion in the global scope, then? Is this something people would ever want to do?

@zanchey
Member
zanchey commented Oct 8, 2014

Space separation sounds like a good idea. In order to avoid confusion, adding a new abbreviation should probably take an unlimited number of arguments, with the first becoming the 'word' and the remainder joined together to become the 'phrase'.

@zanchey zanchey added enhancement and removed needsdocs labels Oct 8, 2014
@kballard
Contributor

There's no need to do any work to "support scoping". If abbr simply uses $fish_user_abbreviations, it will use the global variable if present, or the universal otherwise. Which is exactly how the reader works. The only wrinkle is if the user sets $fish_user_abbreviations as a local variable at the top-level, the reader will actually see that too. And the simple fix is to add the -S flag to the abbr function declaration, which will let it see local variables defined in its parent. But there's definitely no need to try and merge abbreviations (nor any way to do so, short of temporarily deleting the variable at other scopes).

@ridiculousfish
Member

After living on this I think I would like to see abbreviations either underlined or highlighted differently, before they are expanded. This gives me some confidence that I typed them correctly. Thoughts?

@ridiculousfish
Member

Space separator support is in fbade19

@kballard
Contributor

If you hit space and it doesn't expand, then you know you didn't type it correctly. I'm not opposed to underlining or highlighting (it certainly would be an extra cue to let the user know why their commandline just changed), but I'm not sure I understand why you need to have confidence before hitting space.

@kballard
Contributor

@ridiculousfish Regarding fbade19, abbreviations are new enough (and undocumented until recent master) that we should just remove the = support.

@ridiculousfish
Member

I'm thinking about the before-return case, not the before-space case

@dag
Contributor
dag commented Oct 13, 2014

Sounds good to me. I suppose it is possible to go overboard with highlighting, resulting in sluggishness and disorienting information overload, but I don't think that's the case here. Also, I presume, it would use some $fish_color_abbr-or-whatever variable so it could easily be disabled by those who so wish, for whatever reason.

@binaryphile

At first I was going to remark that I found it unintuitive that the highlighting behavior is different for abbreviations vs normal commands, until I realized I was incorrect and that it is consistent.

My confusion came from the fact that I was mixing up highlighting and the suggested-completion feature.

For example, when I type "echo", the highlighting is red until I type the full command, which is as it should be. However the suggested completion successfully anticipates what I was about to type and gives me reassurance that I'm heading in the intended direction.

I receive no such feedback for abbreviations, which is unfortunate since I can't frequently come up with memorable shortenings of commands, particularly the ones which expand to complex commands.

In fact, the goal for me is to pack a wide range of commands into a small namespace (I shoot for ~5 character abbreviations and have hundreds of them). Because of this, I rely even more heavily on shell guidance that I'm headed toward typing a valid command. Something that fulfills the role which suggested completion occupies, but for abbreviations instead of regular commands, would really be helpful, more so than for standard commands even for me.

I can see the difficulty for suggested completions posed by abbreviations. It would be unintuitive for the abbreviation to expand into the actual commands since it would no longer be contiguous with what you are typing. Still, I feel like it could be better than it is now.

At least for me, including the abbreviation itself (not the expansion) in the suggested completions would scratch my itch. I just want to see that I'm correctly typing a prefix of a defined abbreviation. That's enough to tell me that I'm not typing in vain because I've misremembered or mistyped my abbreviation.

@zanchey zanchey added a commit that referenced this issue Oct 17, 2014
@zanchey zanchey web_config: add support for adding and editing abbreviations
Possible future enhancements include explanatory text and an image for
the 'save' action.

Work on #731.
a64c372
@zanchey
Member
zanchey commented Nov 13, 2014

I tried to extend abbr.fish to handle both spaces and = separators, but I haven't had a lot of success in coming up with a robust solution without shelling out to external tools.

@ridiculousfish
Member

I thought the plan was to remove the = support? I was thinking of just doing it, and then posting a message to the fish mailing list with a script that munges $fish_user_abbreviations that people could run to switch to spaces.

@zanchey zanchey added a commit that referenced this issue Nov 15, 2014
@zanchey zanchey abbr/web_config: support space-delimited abbreviations
Support for space-delimited abbreviations was added to the expansion
parser in fbade19; this commit extends that support to the user-facing
tools, and documents the space-separated behaviour. Equals-delimited
abbreviations are expected to be removed before the next release.

Work on #731.
a7bab7b
@zanchey zanchey added a commit that referenced this issue Nov 16, 2014
@zanchey zanchey abbr.fish: improve support for corner cases
Handle unusual cases ('=abc', ' =abc') better - regression from
8e8e6314due to a7bab7b.

Work on #731.
206ea15
@zanchey zanchey added a commit that referenced this issue Nov 16, 2014
@zanchey zanchey abbr.fish: escape the output of abbr --show
Allows abbreviations containing embedded newlines, etc., to be displayed
and exported properly.

Work on #731.
14fa488
@zanchey zanchey added a commit that referenced this issue Nov 16, 2014
@zanchey zanchey web_config: improve abbreviations support
 * Fetch abbreviations by reading the variable directly.
 * Use space separators for writing new abbreviations.

Work on #731.
9aaf93f
@zanchey zanchey modified the milestone: next-minor, next-major Dec 1, 2014
@binaryphile

Hey folks,

I wanted to make a note after having used abbreviations for quite some time now.

I'm a big fan, and they make my life much easier. I especially like the fact that other people can see what commands I'm issuing, in a familiar form.

My only dislike is that there is no history for abbreviations. Since I use abbreviations for almost every command, this has ended up with me losing history functionality for the most part. While you may think that abbreviations should be short enough to not need history functionality, I find myself missing it quite a bit. I have over 200 abbreviations defined, and some of them differ only slightly. Having history show me the one I issued yesterday, for example, would be immensely useful for the more involved and less-frequently used abbreviations. As I practically live with abbreviations, I've had to resort to using functions instead where I need history support since I really don't have history otherwise.

Thanks again for the feature.

@ridiculousfish
Member

@binaryphile Very interesting. It sounds like you have an idea in mind of what this should look like (i.e. the UI). Would you like to describe it in more detail?

@binaryphile

Good question. I guess I'd just like it that if any expansion were triggered then for the abbreviation to go into the history list. So if I my abbreviation were "gclon" for "git clone", then when i hit space (or semicolon), "gclon" would be added to my history. I wouldn't alter the normal behavior of storing the expanded command in history, so both would be available.

After that, when I began typing a prefix of the abbrev (such as "gcl"), I'd expect "gclon" to be the first option for autocompletion to pop up.

Ideally, at that point I'd be able to hit alt-f and have it both complete automatically to the expanded version, plus further offer a new autocompletion of that expanded command from history. I'd want that because I may have entered further arguments after expansion, and would want to have that available in as few keystrokes as possible via the same route I entered it in the first place (abbrev then arguments). Since the abbreviation in the history wouldn't have the arguments, alt-f would be the most natural method for me to move further along the completion argument by argument.

I think ctrl-f wouldn't change and would just move forward to the end of the abbreviation, not the expanded version, since there would be no visual cue as to what you'd be expanding to.

@ElijahLynn

I just got a nightly build up and running to finally test this! I really like this feature and love the fact is has the abbr command that ships with it.

My feedback is that we need a good way to export for dotfile backup. I am thinking these should all go into one file in the format abbr --show outputs. abbr --show is good enough for me for now, I am just going to pipe them into a dotfile for now. But I do want an easy way to import them again when that time comes. So if Fish could import that format now that would be a good next step.

@ElijahLynn

Following the command in the help to add a new abbreviation fails with this error.

selection_235

@zanchey
Member
zanchey commented Mar 6, 2015

Currently you need to enter abbr -a "gco git checkout" but I think it would make more sense to support the case described in the manual.

@ElijahLynn

Ahh, thanks!

@zanchey
Member
zanchey commented Mar 13, 2015

48d3536 makes that change.

@zanchey
Member
zanchey commented Mar 13, 2015

I think we should either leave the '=' separator support in the next minor release and disable it after that, or (silently?) upgrade people's fish_user_abbreviations.

@zanchey
Member
zanchey commented May 4, 2015

I'm going to close this as fixed; I filed #2051 to track the migration. Many thanks to @dag for the concept and all those who tested it - looking forward to seeing this released soon!

@zanchey zanchey closed this May 4, 2015
@ElijahLynn

W00t, thanks everyone, I really LOVE this feature!!

@dideler
Contributor
dideler commented Jul 24, 2016

I'm loving abbreviations, it's a much friendlier experience than aliases. Thanks so much!

I came across this thread while looking up how to store abbreviations in dotfiles1, 2.
If anyone stumbles here for the same reason, you can use abbr -s/--show. For example

abbr --show >> ~/.config/fish/config.fish
@ElijahLynn
ElijahLynn commented Jul 25, 2016 edited

@dideler I am using something similar to that for automated backup, I also recommend piping to sort.

abbr --show | sort > fish_abbreviation_backup;

I put everything into a fish_abbreviation_backup file and then add that to Homeshick for backup. It is all automated though and runs on cron. After I backup I source fish_abbreviation_backup so that fish shows alphabetical order when doing an abbr --show.

Would be nice if fish kept abbreviations in a file, similar to how it does things with functions.

update: Issue to sort abbreviations when storing them -> #2156

@krader1961
Member

Would be nice if fish kept abbreviations in a file...

From the man page: "Abbreviations are stored using universal variables." Which means you'll find them in the ~/.config/fish/fishd.macaddr file under var name fish_user_abbreviations. The abbr command simply manipulates that universal var.

@ElijahLynn

Thanks, would it be okay to consider the idea of keeping them in ~/.config/fish/abbreviations.fish so we can add them easily to our dotfiles?

@faho
Member
faho commented Sep 2, 2016

@ElijahLynn: What I do is I have a file called "abbrs.fish" in ~/.config/fish/conf.d/, with the following contents:

if not set -q fish_initialized
    abbr -a alsamixer alsamixer -c0
    abbr -a e emacs -nw
    abbr -a \$PAGER less
    abbr -a mu4e emacs --eval "\(mu4e\)"
    abbr -a pm pulsemixer
    abbr -a rm rm -I
    abbr -a sc systemctl
    abbr -a upo upower -i /org/freedesktop/UPower/devices/battery_BAT0
    abbr -a usc systemctl --user
    # Double-escaping needed
    abbr -a d2 env WINEPREFIX=/home/alfa/.wine32/ wine ~/.wine/drive_c/Program\\ Files\\ \\(x86\\)/Diablo\\ II/Diablo\\ II.exe
    abbr -a c curl -LO -C -
    set -U  fish_user_paths ~alfa/.local/bin $GOPATH/bin
    set -U fish_initialized
end

I have this in my dotfiles, and I can just drop it into any machine I haven't yet used it on, and if I want to reset the abbrs, I can erase them all (I do set -e fish_user_abbreviations; set -e fish_initialized) and restart fish.

@ElijahLynn

Thanks, I may be missing this but that looks like you need maintain that manually and don't get to add manage them easily with the abbr command.

@faho
Member
faho commented Sep 2, 2016

You add the abbr command there. This can even be the output of an abbr --show call.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment