function abbr --description "Manage abbreviations" # parse arguments set -l mode set -l mode_flag # the flag that was specified, for better errors set -l mode_arg set -l needs_arg no while set -q argv[1] if test $needs_arg = single set mode_arg $argv[1] set needs_arg no else if test $needs_arg = coalesce set mode_arg "$argv" set needs_arg no set -e argv else set -l new_mode switch $argv[1] case '-h' '--help' __fish_print_help abbr return 0 case '-a' '--add' set new_mode add set needs_arg coalesce case '-e' '--erase' set new_mode erase set needs_arg single case '-l' '--list' set new_mode list case '-s' '--show' set new_mode show case '--' set -e argv[1] break case '-*' printf ( _ "%s: invalid option -- %s\n" ) abbr $argv[1] >&2 return 1 case '*' break end if test -n "$mode" -a -n "$new_mode" # we're trying to set two different modes printf ( _ "%s: %s cannot be specified along with %s\n" ) abbr $argv[1] $mode_flag >&2 return 1 end set mode $new_mode set mode_flag $argv[1] end set -e argv[1] end if test $needs_arg != no printf ( _ "%s: option requires an argument -- %s\n" ) abbr $mode_flag >&2 return 1 end # If run with no options, treat it like --add if we have an argument, or # --show if we do not have an argument if test -z "$mode" if set -q argv[1] set mode 'add' set mode_arg "$argv" set -e argv else set mode 'show' end end # none of our modes want any excess arguments if set -q argv[1] printf ( _ "%s: Unexpected argument -- %s\n" ) abbr $argv[1] >&2 return 1 end switch $mode case 'add' set -l key set -l value __fish_abbr_parse_entry $mode_arg key value # ensure the key contains at least one non-space character set -l IFS \n\ \t printf '%s' $key | read -lz key_ __ if test -z "$key_" printf ( _ "%s: abbreviation must have a non-empty key\n" ) abbr >&2 return 1 end if test -z "$value" printf ( _ "%s: abbreviation must have a value\n" ) abbr >&2 return 1 end if set -l idx (__fish_abbr_get_by_key $key) # erase the existing abbreviation set -e fish_user_abbreviations[$idx] end if not set -q fish_user_abbreviations # initialize as a universal variable, so we can skip the -U later # and therefore work properly if someone sets this as a global variable set -U fish_user_abbreviations end set fish_user_abbreviations $fish_user_abbreviations $mode_arg return 0 case 'erase' set -l key __fish_abbr_parse_entry $mode_arg key if set -l idx (__fish_abbr_get_by_key $key) set -e fish_user_abbreviations[$idx] return 0 else printf ( _ "%s: no such abbreviation '%s'\n" ) abbr $key >&2 return 2 end case 'show' for i in $fish_user_abbreviations # Disable newline splitting set -lx IFS '' __fish_abbr_parse_entry $i key value # Check to see if either key or value has a leading dash # If so, we need to write -- set -l opt_double_dash '' switch $key ; case '-*'; set opt_double_dash ' --'; end switch $value ; case '-*'; set opt_double_dash ' --'; end echo abbr$opt_double_dash (__fish_abbr_escape "$key") (__fish_abbr_escape "$value") end return 0 case 'list' for i in $fish_user_abbreviations set -l key __fish_abbr_parse_entry $i key printf "%s\n" $key end return 0 end end function __fish_abbr_escape # Prettify the common case: if everything is alphanumeric, # we do not need escapes. # Do this by deleting alnum characters, and check if there's anything left. # Note we need to preserve spaces, so spaces are not considered alnum if test -z (echo -n "$argv" | tr -d '[:alnum:]_') echo $argv else # Escape via single quotes # printf is nice for stripping the newline that sed outputs printf "'%s'" (echo -n $argv | sed -e s,\\\\,\\\\\\\\,g -e s,\',\\\\\',g) end end function __fish_abbr_get_by_key if not set -q argv[1] echo "__fish_abbr_get_by_key: expected one argument, got none" >&2 return 2 end set -l count (count $fish_user_abbreviations) if test $count -gt 0 set -l key __fish_abbr_parse_entry $argv[1] key set -l IFS \n # ensure newline splitting is enabled for i in (seq $count) set -l key_i __fish_abbr_parse_entry $fish_user_abbreviations[$i] key_i if test "$key" = "$key_i" echo $i return 0 end end end return 1 end function __fish_abbr_parse_entry -S -a __input __key __value if test -z "$__key" set __key __ end if test -z "$__value" set __value __ end set -l IFS '= ' switch $__input case '=*' # read will skip any leading ='s, but we don't want that set __input " $__input" set __key _ set IFS '=' case ' =*' set __key _ set IFS '=' end # use read -z to avoid splitting on newlines # I think we can safely assume there will be no NULs in the input printf "%s" $__input | read -z $__key $__value return 0 end