Revert "rewrite abbr function"

This reverts commit 17dff8c569.
It was meant for the major branch.
This commit is contained in:
Kurtis Rader 2017-08-13 15:44:31 -07:00
parent e6fbb93d31
commit ef25d8c76d
12 changed files with 322 additions and 620 deletions

View file

@ -2,57 +2,54 @@
\subsection abbr-synopsis Synopsis
\fish{synopsis}
abbr --add [SCOPE] WORD EXPANSION
abbr --erase word
abbr --rename [SCOPE] OLD_WORD NEW_WORD
abbr --add word phrase...
abbr --rename word new_word
abbr --show
abbr --list
abbr --erase word
\endfish
\subsection abbr-description Description
Abbreviations are user-defined character sequences or words that are replaced with longer phrases after they are entered. For example, a frequently-run command such as `git checkout` can be abbreviated to `gco`. After entering `gco` and pressing @key{Space} or @key{Enter}, the full text `git checkout` will appear in the command line. The `abbr` command manipulates those abbreviations.
`abbr` manipulates the list of abbreviations that fish will expand.
Each abbreviation is stored in its own global or universal variable. The name consists of the prefix `_fish_abbr_` followed by the WORD after being transformed by `string escape style=var`. The WORD cannot contain a space but all other characters are legal.
Abbreviations are user-defined character sequences or words that are replaced with longer phrases after they are entered. For example, a frequently-run command such as `git checkout` can be abbreviated to `gco`. After entering `gco` and pressing @key{Space} or @key{Enter}, the full text `git checkout` will appear in the command line.
Defining an abbreviation with global scope is slightly faster than universal scope (which is the default). But in general you'll only want to use the global scope when defining abbreviations in a startup script like `~/.config/fish/config.fish` like this:
Abbreviations are stored in a variable named `fish_user_abbreviations`. This is automatically created as a universal variable the first time an abbreviation is created. If you want your abbreviations to be private to a particular fish session you can put the following in your *~/.config/fish/config.fish* file before you define your first abbrevation:
\fish
if status --is-interactive
abbr --add --global first 'echo my first abbreviation'
abbr --add --global second 'echo my second abbreviation'
abbr --add --global gco git checkout
set -g fish_user_abbreviations
abbr --add first 'echo my first abbreviation'
abbr --add second 'echo my second abbreviation'
# etcetera
end
\endfish
You can create abbreviations interactively and they will be visible to other fish sessions if you use the `-U` or `--universal` flag or don't explicitly specify the scope and the abbreviation isn't already defined with global scope. If you want it to be visible only to the current shell use the `-g` or `--global` flag.
You can create abbreviations directly on the command line and they will be saved automatically and made visible to other fish sessions if `fish_user_abbreviations` is a universal variable. If you keep the variable as universal, `abbr --add` statements in <a href="tutorial.html#tut_startup">config.fish</a> will do nothing but slow down startup slightly.
\subsection abbr-options Options
The following options are available:
The following parameters are available:
- `-a WORD EXPANSION` or `--add WORD EXPANSION` Adds a new abbreviation, causing WORD to be expanded to PHRASE. You can optionally specify `-g` or `--global` to avoid the overhead of universal variables at the expense of not having the definition being immediately visible to other fish shells that are already running. If you don't specify global scope it default to universal. For clarity you can also specify `-U` or `--universal`.
- `-a WORD PHRASE` or `--add WORD PHRASE` Adds a new abbreviation, causing WORD to be expanded to PHRASE.
- `-r OLD_WORD NEW_WORD` or `--rename OLD_WORD NEW_WORD` Renames an abbreviation, from OLD_WORD to NEW_WORD.
- `-r WORD NEW_WORD` or `--rename WORD NEW_WORD` Renames an abbreviation, from WORD to NEW_WORD.
- `-s` or `--show` Show all abbreviations in a manner suitable for export and import.
- `-s` or `--show` Show all abbreviated words and their expanded phrases in a manner suitable for export and import.
- `-l` or `--list` Lists all abbreviated words.
- `-e WORD` or `--erase WORD` Erase the abbreviation WORD.
Note: fish version 2.1 supported `-a WORD=PHRASE`. This syntax is now deprecated but will still be converted.
\subsection abbr-example Examples
\fish
abbr -a -g gco git checkout
abbr -a gco git checkout
\endfish
Add a new abbreviation where `gco` will be replaced with `git checkout` global to the current shell. This abbreviation will not be automatically visible to other shells unless the same command is run in those shells (such as when executing the commands in config.fish).
\fish
abbr -a -U l less
\endfish
Add a new abbreviation where `l` will be replaced with `less` univeral so all shells. Note that you omit the `-U` since it is the default.
Add a new abbreviation where `gco` will be replaced with `git checkout`.
\fish
abbr -r gco gch

View file

@ -4,15 +4,6 @@
# This function is called by the __fish_on_interactive function, which is defined in config.fish.
#
function __fish_config_interactive -d "Initializations that should be performed when entering interactive mode"
if not set -q __fish_init_3_x
# Perform transitions relevant to going from fish 2.x to 3.x.
# Migrate old universal abbreviations to the new scheme.
abbr_old | source
set -U __fish_init_3_x
end
# Make sure this function is only run once.
if set -q __fish_config_interactive_done
return

View file

@ -1,201 +1,206 @@
function abbr --description "Manage abbreviations using new fish 3.0 scheme."
set -l options --stop-nonopt --exclusive 'a,r,e,l,s' --exclusive 'g,U'
set options $options 'h/help' 'a/add' 'r/rename' 'e/erase' 'l/list' 's/show'
set options $options 'g/global' 'U/universal'
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]
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 multi
case '-r' '--rename'
set new_mode rename
set needs_arg double
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]
set -e argv[1]
end
argparse -n abbr $options -- $argv
or return
# If run with no options, treat it like --add if we have an argument, or
# --show if we do not have an argument
if not set -q mode[1]
if set -q argv[1]
set mode add
set needs_arg multi
else
set mode show
end
end
if set -q _flag_help
__fish_print_help abbr
if test $needs_arg = single
set mode_arg $argv[1]
set needs_arg no
set -e argv[1]
else if test $needs_arg = double
# Pull the two parameters from argv.
# * leave argv non-empty, if there are more than two arguments
# * leave needs_arg set to double if there is not enough arguments
if set -q argv[1]
set param1 $argv[1]
set -e argv[1]
if set -q argv[1]
set param2 $argv[1]
set needs_arg no
set -e argv[1]
end
end
else if test $needs_arg = multi
set mode_arg $argv
set needs_arg no
set -e argv
end
if test $needs_arg != no
printf ( _ "%s: option requires an argument -- %s\n" ) abbr $mode_flag >&2
return 1
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'
# Convert from old "key=value" syntax
# TODO: This should be removed later
if not set -q mode_arg[2]
and string match -qr '^[^ ]+=' -- $mode_arg
set mode_arg (string split "=" -- $mode_arg)
end
# Bail out early if the exact abbr is already in
set -q fish_user_abbreviations
and contains -- "$mode_arg" $fish_user_abbreviations
and return 0
set -l key $mode_arg[1]
set -e mode_arg[1]
set -l value "$mode_arg"
# Because we later store "$key $value", there can't be any spaces in the key
if string match -q "* *" -- $key
printf ( _ "%s: abbreviation cannot have spaces in the 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 "$key $value"
return 0
case 'rename'
set -l old_name $param1
set -l new_name $param2
# if the target name already exists, throw an error
if set -l idx (__fish_abbr_get_by_key $new_name)
printf ( _ "%s: abbreviation '%s' already exists, cannot rename\n" ) abbr $new_name >&2
return 2
end
# Because we later store "$key $value", there can't be any spaces in the key
if string match -q "* *" -- $new_name
printf ( _ "%s: abbreviation cannot have spaces in the key\n" ) abbr >&2
return 1
end
set -l idx (__fish_abbr_get_by_key $old_name)
or begin
printf ( _ "%s: no such abbreviation '%s'\n" ) abbr $old_name >&2
return 2
end
set -l value (string split " " -m 1 -- $fish_user_abbreviations[$idx])[2]
set fish_user_abbreviations[$idx] "$new_name $value"
return 0
case 'erase'
if set -l idx (__fish_abbr_get_by_key $mode_arg)
set -e fish_user_abbreviations[$idx]
return 0
else
printf ( _ "%s: no such abbreviation '%s'\n" ) abbr $mode_arg >&2
return 2
end
case 'show'
for i in $fish_user_abbreviations
set -l opt_double_dash
set -l kv (string split " " -m 1 -- $i)
set -l key $kv[1]
set -l value $kv[2]
# Check to see if either key or value has a leading dash
# If so, we need to write --
string match -q -- '-*' $key $value
and set opt_double_dash '--'
echo abbr $opt_double_dash (string escape -- $key $value)
end
return 0
case 'list'
for i in $fish_user_abbreviations
set -l key (string split " " -m 1 -- $i)[1]
printf "%s\n" $key
end
return 0
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 -q fish_user_abbreviations
or return 1
# Going through all entries is still quicker than calling `seq`
set -l keys
for kv in $fish_user_abbreviations
# If this does not match, we have screwed up before and the error should be reported
set keys $keys (string split " " -m 1 -- $kv)[1]
end
if set -l idx (contains -i -- $argv[1] $keys)
echo $idx
return 0
end
# If run with no options, treat it like --add if we have arguments, or
# --show if we do not have any arguments.
set -l _flag_add
set -l _flag_show
if not set -q _flag_add[1]
and not set -q _flag_rename[1]
and not set -q _flag_erase[1]
and not set -q _flag_list[1]
and not set -q _flag_show[1]
if set -q argv[1]
set _flag_add --add
else
set _flag_show --show
end
end
set -l abbr_scope
if set -q _flag_global
set abbr_scope --global
else if set -q _flag_universal
set abbr_scope --universal
end
if set -q _flag_add[1]
__fish_abbr_add $argv
return
else if set -q _flag_erase[1]
__fish_abbr_erase $argv
return
else if set -q _flag_rename[1]
__fish_abbr_rename $argv
return
else if set -q _flag_list[1]
__fish_abbr_list $argv
return
else if set -q _flag_show[1]
__fish_abbr_show $argv
return
else
printf ( _ "%s: Could not figure out what to do!\n" ) abbr >&2
return 127
end
end
function __fish_abbr_add --no-scope-shadowing
if not set -q argv[2]
printf ( _ "%s %s: Requires at least two arguments\n" ) abbr --add >&2
return 1
end
# Because of the way abbreviations are expanded there can't be any spaces in the key.
set -l abbr_name $argv[1]
set -l escaped_abbr_name (string escape -- $abbr_name)
if string match -q "* *" -- $abbr_name
set -l msg ( _ "%s %s: Abbreviation %s cannot have spaces in the word\n" )
printf $msg abbr --add $escaped_abbr_name >&2
return 1
end
set -l abbr_val "$argv[2..-1]"
set -l abbr_var_name _fish_abbr_(string escape --style=var -- $abbr_name)
if not set -q $abbr_var_name
# We default to the universal scope if the user didn't explicitly specify a scope and the
# abbreviation isn't already defined.
set -q abbr_scope[1]
or set abbr_scope --universal
end
true # make sure the next `set` command doesn't leak the previous status
set $abbr_scope $abbr_var_name $abbr_val
end
function __fish_abbr_erase --no-scope-shadowing
if set -q argv[2]
printf ( _ "%s %s: Expected one argument\n" ) abbr --erase >&2
return 1
end
# Because of the way abbreviations are expanded there can't be any spaces in the key.
set -l abbr_name $argv[1]
set -l escaped_name (string escape -- $abbr_name)
if string match -q "* *" -- $abbr_old_name
set -l msg ( _ "%s %s: Abbreviation %s cannot have spaces in the word\n" )
printf $msg abbr --erase $escaped_name >&2
return 1
end
set -l abbr_var_name _fish_abbr_(string escape --style=var -- $abbr_name)
if not set -q $abbr_var_name
printf ( _ "%s %s: No abbreviation named %s\n" ) abbr --erase $escaped_name >&2
return 121
end
set -e $abbr_var_name
end
function __fish_abbr_rename --no-scope-shadowing
if test (count $argv) -ne 2
printf ( _ "%s %s: Requires exactly two arguments\n" ) abbr --rename >&2
return 1
end
set -l old_name $argv[1]
set -l new_name $argv[2]
set -l escaped_old_name (string escape -- $old_name)
set -l escaped_new_name (string escape -- $new_name)
if string match -q "* *" -- $old_name
set -l msg ( _ "%s %s: Abbreviation %s cannot have spaces in the word\n" )
printf $msg abbr --rename $escaped_old_name >&2
return 1
end
if string match -q "* *" -- $new_name
set -l msg ( _ "%s %s: Abbreviation %s cannot have spaces in the word\n" )
printf $msg abbr --rename $escaped_new_name >&2
return 1
end
set -l old_var_name _fish_abbr_(string escape --style=var -- $old_name)
set -l new_var_name _fish_abbr_(string escape --style=var -- $new_name)
if not set -q $old_var_name
printf ( _ "%s %s: No abbreviation named %s\n" ) abbr --rename $escaped_old_name >&2
return 1
end
if set -q $new_var_name
set -l msg ( _ "%s %s: Abbreviation %s already exists, cannot rename %s\n" )
printf $msg abbr --rename $escaped_new_name $escaped_old_name >&2
return 1
end
set -l old_var_val $$old_var_name
if not set -q abbr_scope[1]
# User isn't forcing the scope so use the existing scope.
if set -ql $old_var_name
set abbr_scope --global
else
set abbr_scope --universal
end
end
set -e $old_var_name
set $abbr_scope $new_var_name $old_var_val
end
function __fish_abbr_list --no-scope-shadowing
if set -q argv[1]
printf ( _ "%s %s: Unexpected argument -- '%s'\n" ) abbr --erase $argv[1] >&2
return 1
end
for var_name in (set --names)
string match -q '_fish_abbr_*' $var_name
or continue
set -l abbr_name (string unescape --style=var (string sub -s 12 $var_name))
echo $abbr_name
end
end
function __fish_abbr_show --no-scope-shadowing
if set -q argv[1]
printf ( _ "%s %s: Unexpected argument -- '%s'\n" ) abbr --erase $argv[1] >&2
return 1
end
for var_name in (set --names)
string match -q '_fish_abbr_*' $var_name
or continue
set -l abbr_var_name $var_name
set -l abbr_name (string unescape --style=var -- (string sub -s 12 $abbr_var_name))
set -l abbr_name (string escape --style=script -- $abbr_name)
set -l abbr_val $$abbr_var_name
set -l abbr_val (string escape --style=script -- $abbr_val)
if set -ql $abbr_var_name
printf 'abbr -a %s -- %s %s\n' -l $abbr_name $abbr_val
end
if set -qg $abbr_var_name
printf 'abbr -a %s -- %s %s\n' -g $abbr_name $abbr_val
end
if set -qU $abbr_var_name
printf 'abbr -a %s -- %s %s\n' -U $abbr_name $abbr_val
end
end
return 1
end

View file

@ -1,206 +0,0 @@
function abbr_old --description "Manage abbreviations using old fish 2.x scheme."
# 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]
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 multi
case '-r' '--rename'
set new_mode rename
set needs_arg double
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]
set -e argv[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 not set -q mode[1]
if set -q argv[1]
set mode add
set needs_arg multi
else
set mode show
end
end
if test $needs_arg = single
set mode_arg $argv[1]
set needs_arg no
set -e argv[1]
else if test $needs_arg = double
# Pull the two parameters from argv.
# * leave argv non-empty, if there are more than two arguments
# * leave needs_arg set to double if there is not enough arguments
if set -q argv[1]
set param1 $argv[1]
set -e argv[1]
if set -q argv[1]
set param2 $argv[1]
set needs_arg no
set -e argv[1]
end
end
else if test $needs_arg = multi
set mode_arg $argv
set needs_arg no
set -e argv
end
if test $needs_arg != no
printf ( _ "%s: option requires an argument -- %s\n" ) abbr $mode_flag >&2
return 1
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'
# Convert from old "key=value" syntax
# TODO: This should be removed later
if not set -q mode_arg[2]
and string match -qr '^[^ ]+=' -- $mode_arg
set mode_arg (string split "=" -- $mode_arg)
end
# Bail out early if the exact abbr is already in
set -q fish_user_abbreviations
and contains -- "$mode_arg" $fish_user_abbreviations
and return 0
set -l key $mode_arg[1]
set -e mode_arg[1]
set -l value "$mode_arg"
# Because we later store "$key $value", there can't be any spaces in the key
if string match -q "* *" -- $key
printf ( _ "%s: abbreviation cannot have spaces in the 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 "$key $value"
return 0
case 'rename'
set -l old_name $param1
set -l new_name $param2
# if the target name already exists, throw an error
if set -l idx (__fish_abbr_get_by_key $new_name)
printf ( _ "%s: abbreviation '%s' already exists, cannot rename\n" ) abbr $new_name >&2
return 2
end
# Because we later store "$key $value", there can't be any spaces in the key
if string match -q "* *" -- $new_name
printf ( _ "%s: abbreviation cannot have spaces in the key\n" ) abbr >&2
return 1
end
set -l idx (__fish_abbr_get_by_key $old_name)
or begin
printf ( _ "%s: no such abbreviation '%s'\n" ) abbr $old_name >&2
return 2
end
set -l value (string split " " -m 1 -- $fish_user_abbreviations[$idx])[2]
set fish_user_abbreviations[$idx] "$new_name $value"
return 0
case 'erase'
if set -l idx (__fish_abbr_get_by_key $mode_arg)
set -e fish_user_abbreviations[$idx]
return 0
else
printf ( _ "%s: no such abbreviation '%s'\n" ) abbr $mode_arg >&2
return 2
end
case 'show'
for i in $fish_user_abbreviations
set -l opt_double_dash
set -l kv (string split " " -m 1 -- $i)
set -l key $kv[1]
set -l value $kv[2]
# Check to see if either key or value has a leading dash
# If so, we need to write --
string match -q -- '-*' $key $value
and set opt_double_dash '--'
echo abbr $opt_double_dash (string escape -- $key $value)
end
return 0
case 'list'
for i in $fish_user_abbreviations
set -l key (string split " " -m 1 -- $i)[1]
printf "%s\n" $key
end
return 0
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 -q fish_user_abbreviations
or return 1
# Going through all entries is still quicker than calling `seq`
set -l keys
for kv in $fish_user_abbreviations
# If this does not match, we have screwed up before and the error should be reported
set keys $keys (string split " " -m 1 -- $kv)[1]
end
if set -l idx (contains -i -- $argv[1] $keys)
echo $idx
return 0
end
return 1
end

View file

@ -41,7 +41,6 @@
#include "env.h"
#include "env_universal_common.h"
#include "event.h"
#include "expand.h"
#include "fallback.h" // IWYU pragma: keep
#include "fish_version.h"
#include "history.h"
@ -188,7 +187,7 @@ void var_stack_t::push(bool new_scope) {
// i.e. not if it's just `begin; end` or "--no-scope-shadowing".
if (new_scope) {
if (!(top_node == this->global_env)) {
for (auto &var : top_node->env) {
for (auto& var : top_node->env) {
if (var.second.exportv) {
// This should copy var
node->env.insert(var);
@ -604,7 +603,7 @@ static bool variable_is_colon_delimited_var(const wcstring &str) {
}
/// React to modifying the given variable.
static void react_to_variable_change(const wchar_t *op, const wcstring &key) {
static void react_to_variable_change(const wcstring &key) {
// Don't do any of this until `env_init()` has run. We only want to do this in response to
// variables set by the user; e.g., in a script like *config.fish* or interactively or as part
// of loading the universal variables for the first time.
@ -632,36 +631,37 @@ static void react_to_variable_change(const wchar_t *op, const wcstring &key) {
env_set_read_limit();
} else if (key == L"FISH_HISTORY") {
reader_change_history(history_session_id().c_str());
} else if (wcsncmp(key.c_str(), L"_fish_abbr_", wcslen(L"_fish_abbr_")) == 0) {
update_abbr_cache(op, key);
}
}
/// Universal variable callback function. This function makes sure the proper events are triggered
/// when an event occurs.
static void universal_callback(fish_message_type_t type, const wchar_t *name) {
const wchar_t *op;
const wchar_t *str = NULL;
switch (type) {
case SET:
case SET_EXPORT: {
op = L"SET";
str = L"SET";
break;
}
case ERASE: {
op = L"ERASE";
str = L"ERASE";
break;
}
}
react_to_variable_change(op, name);
vars_stack().mark_changed_exported();
if (str) {
vars_stack().mark_changed_exported();
event_t ev = event_t::variable_event(name);
ev.arguments.push_back(L"VARIABLE");
ev.arguments.push_back(op);
ev.arguments.push_back(name);
event_fire(&ev);
event_t ev = event_t::variable_event(name);
ev.arguments.push_back(L"VARIABLE");
ev.arguments.push_back(str);
ev.arguments.push_back(name);
event_fire(&ev);
}
if (name) react_to_variable_change(name);
}
/// Make sure the PATH variable contains something.
@ -1130,7 +1130,7 @@ int env_set(const wcstring &key, const wchar_t *val, env_mode_flags_t var_mode)
event_fire(&ev);
// debug( 1, L"env_set: return from event firing" );
react_to_variable_change(L"SET", key);
react_to_variable_change(key);
return ENV_OK;
}
@ -1203,7 +1203,7 @@ int env_remove(const wcstring &key, int var_mode) {
if (is_exported) vars_stack().mark_changed_exported();
}
react_to_variable_change(L"ERASE", key);
react_to_variable_change(key);
return !erased;
}

View file

@ -1572,39 +1572,37 @@ bool fish_xdm_login_hack_hack_hack_hack(std::vector<std::string> *cmds, int argc
return result;
}
std::map<const wcstring, const wcstring> abbreviations;
void update_abbr_cache(const wchar_t *op, const wcstring varname) {
wcstring abbr;
if (!unescape_string(varname.substr(wcslen(L"_fish_abbr_")), &abbr, 0, STRING_STYLE_VAR)) {
debug(1, L"Abbreviation var '%ls' is not correctly encoded, ignoring it.", varname.c_str());
return;
}
abbreviations.erase(abbr);
if (wcscmp(op, L"ERASE") != 0) {
const env_var_t expansion = env_get_string(varname);
if (!expansion.missing_or_empty()) {
abbreviations.emplace(std::make_pair(abbr, expansion));
}
}
}
bool expand_abbreviation(const wcstring &src, wcstring *output) {
if (src.empty()) return false;
auto abbr = abbreviations.find(src);
if (abbr == abbreviations.end()) return false;
if (output != NULL) output->assign(abbr->second);
return true;
// Get the abbreviations. Return false if we have none.
env_var_t abbrs = env_get_string(USER_ABBREVIATIONS_VARIABLE_NAME);
if (abbrs.missing_or_empty()) return false;
#if 0
for (auto abbr : abbreviations) {
if (src == abbr.first) {
// We found a matching abbreviation. Set output to the expansion.
if (output != NULL) output->assign(abbr.second);
return true;
bool result = false;
std::vector<wcstring> abbrsv;
tokenize_variable_array(abbrs, abbrsv);
for (auto abbr : abbrsv) {
// Abbreviation is expected to be of the form 'foo=bar' or 'foo bar'. Parse out the first =
// or space. Silently skip on failure (no equals, or equals at the end or beginning). Try to
// avoid copying any strings until we are sure this is a match.
size_t equals_pos = abbr.find(L'=');
size_t space_pos = abbr.find(L' ');
size_t separator = mini(equals_pos, space_pos);
if (separator == wcstring::npos || separator == 0 || separator + 1 == abbr.size()) continue;
// Find the character just past the end of the command. Walk backwards, skipping spaces.
size_t cmd_end = separator;
while (cmd_end > 0 && iswspace(abbr.at(cmd_end - 1))) cmd_end--;
// See if this command matches.
if (abbr.compare(0, cmd_end, src) == 0) {
// Success. Set output to everything past the end of the string.
if (output != NULL) output->assign(abbr, separator + 1, wcstring::npos);
result = true;
break;
}
}
return false;
#endif
return result;
}

View file

@ -135,10 +135,11 @@ wcstring replace_home_directory_with_tilde(const wcstring &str);
/// Abbreviation support. Expand src as an abbreviation, returning true if one was found, false if
/// not. If result is not-null, returns the abbreviation by reference.
void update_abbr_cache(const wchar_t *op, const wcstring varnam);
#define USER_ABBREVIATIONS_VARIABLE_NAME L"fish_user_abbreviations"
bool expand_abbreviation(const wcstring &src, wcstring *output);
// Terrible hacks
bool fish_xdm_login_hack_hack_hack_hack(std::vector<std::string> *cmds, int argc,
const char *const *argv);
#endif

View file

@ -1610,15 +1610,21 @@ static void test_fuzzy_match(void) {
static void test_abbreviations(void) {
say(L"Testing abbreviations");
const wchar_t *abbreviations =
L"gc=git checkout" ARRAY_SEP_STR
L"foo=" ARRAY_SEP_STR
L"gc=something else" ARRAY_SEP_STR
L"=" ARRAY_SEP_STR
L"=foo" ARRAY_SEP_STR
L"foo" ARRAY_SEP_STR
L"foo=bar" ARRAY_SEP_STR
L"gx git checkout";
env_push(true);
const std::vector<std::pair<const wcstring, const wcstring>> abbreviations = {
{L"gc", L"git checkout"}, {L"foo", L"bar"}, {L"gx", L"git checkout"},
};
for (auto it : abbreviations) {
int ret = env_set(L"_fish_abbr_" + it.first, it.second.c_str(), ENV_LOCAL);
if (ret != 0) err(L"Unable to set abbreviation variable");
}
int ret = env_set(USER_ABBREVIATIONS_VARIABLE_NAME, abbreviations, ENV_LOCAL);
if (ret != 0) err(L"Unable to set abbreviation variable");
wcstring result;
if (expand_abbreviation(L"", &result)) err(L"Unexpected success with empty abbreviation");

View file

@ -1,52 +1,7 @@
####################
# Test basic add and list of __abbr1
####################
# Erasing one that doesn't exist should do nothing
abbr --erase: No abbreviation named NOT_AN_ABBR
####################
# Adding existing __abbr1 should be idempotent
####################
# Replacing __abbr1 definition
####################
# __abbr1 -s and --show tests
####################
# Test erasing __abbr1
####################
# Ensure we escape special characters on output
####################
# Ensure we handle leading dashes in abbreviation names properly
####################
# Test that an abbr word containing spaces is rejected
abbr --add: Abbreviation 'a b c' cannot have spaces in the word
####################
# Test renaming
####################
# Test renaming a nonexistent abbreviation
abbr --rename: No abbreviation named __abbr6
####################
# Test renaming to a abbreviation with spaces
abbr --rename: Abbreviation 'g h i' cannot have spaces in the word
####################
# Test renaming without arguments
abbr --rename: Requires exactly two arguments
####################
# Test renaming with too many arguments
abbr --rename: Requires exactly two arguments
####################
# Test renaming to existing abbreviation
abbr --rename: Abbreviation __abbr12 already exists, cannot rename __abbr11
abbr: no such abbreviation 'NOT_AN_ABBR'
abbr: abbreviation cannot have spaces in the key
abbr: no such abbreviation '__abbr6'
abbr: abbreviation cannot have spaces in the key
abbr: option requires an argument -- -r
abbr: Unexpected argument -- __abbr10
abbr: abbreviation '__abbr12' already exists, cannot rename

View file

@ -1,42 +1,44 @@
logmsg Test basic add and list of __abbr1
# Test basic add and list
abbr __abbr1 alpha beta gamma
abbr | grep __abbr1
logmsg Erasing one that doesn\'t exist should do nothing
# Erasing one that doesn't exist should do nothing
abbr --erase NOT_AN_ABBR
abbr | grep __abbr1
logmsg Adding existing __abbr1 should be idempotent
# Adding existing one should be idempotent
abbr __abbr1 alpha beta gamma
abbr | grep __abbr1
logmsg Replacing __abbr1 definition
# Replacing
abbr __abbr1 delta
abbr | grep __abbr1
logmsg __abbr1 -s and --show tests
# -s and --show tests
abbr -s | grep __abbr1
abbr --show | grep __abbr1
logmsg Test erasing __abbr1
# Test erasing
abbr -e __abbr1
abbr | grep __abbr1
logmsg Ensure we escape special characters on output
# Ensure we escape special characters on output
abbr '~__abbr2' '$xyz'
abbr | grep __abbr2
abbr -e '~__abbr2'
logmsg Ensure we handle leading dashes in abbreviation names properly
# Ensure we handle leading dashes in abbreviation names properly
abbr -- '--__abbr3' 'xyz'
abbr | grep __abbr3
abbr -e -- '--__abbr3'
logmsg Test that an abbr word containing spaces is rejected
abbr "a b c" "d e f"
abbr | grep 'a b c'
# Ensure we are not recognizing later "=" as separators
abbr d2 env a=b banana
abbr -l | string match -q d2; or echo "= test failed"
logmsg Test renaming
abbr "a b c" "d e f"; or true
# Test renaming
abbr __abbr4 omega
abbr | grep __abbr5
abbr -r __abbr4 __abbr5
@ -44,28 +46,26 @@ abbr | grep __abbr5
abbr -e __abbr5
abbr | grep __abbr4
logmsg Test renaming a nonexistent abbreviation
abbr -r __abbr6 __abbr
# Test renaming a nonexistent abbreviation
abbr -r __abbr6 __abbr; or true
logmsg Test renaming to a abbreviation with spaces
# Test renaming to a abbreviation with spaces
abbr __abbr4 omega
abbr -r __abbr4 "g h i"
abbr -r __abbr4 "g h i"; or true
abbr -e __abbr4
logmsg Test renaming without arguments
# Test renaming without arguments
abbr __abbr7 omega
abbr -r __abbr7
abbr -r __abbr7; or true
logmsg Test renaming with too many arguments
# Test renaming with too many arguments
abbr __abbr8 omega
abbr -r __abbr8 __abbr9 __abbr10
abbr -r __abbr8 __abbr9 __abbr10; or true
abbr | grep __abbr8
abbr | grep __abbr9
abbr | grep __abbr10
abbr | grep __abbr9; or true
abbr | grep __abbr10; or true
logmsg Test renaming to existing abbreviation
# Test renaming to existing abbreviation
abbr __abbr11 omega11
abbr __abbr12 omega12
abbr -r __abbr11 __abbr12
exit 0 # the last `abbr` command is expected to fail -- don't let that cause a test failure
abbr -r __abbr11 __abbr12; or true

View file

@ -1,55 +1,10 @@
####################
# Test basic add and list of __abbr1
abbr -a -U -- __abbr1 'alpha beta gamma'
####################
# Erasing one that doesn't exist should do nothing
abbr -a -U -- __abbr1 'alpha beta gamma'
####################
# Adding existing __abbr1 should be idempotent
abbr -a -U -- __abbr1 'alpha beta gamma'
####################
# Replacing __abbr1 definition
abbr -a -U -- __abbr1 delta
####################
# __abbr1 -s and --show tests
abbr -a -U -- __abbr1 delta
abbr -a -U -- __abbr1 delta
####################
# Test erasing __abbr1
####################
# Ensure we escape special characters on output
abbr -a -U -- '~__abbr2' '$xyz'
####################
# Ensure we handle leading dashes in abbreviation names properly
abbr -a -U -- --__abbr3 xyz
####################
# Test that an abbr word containing spaces is rejected
####################
# Test renaming
abbr -a -U -- __abbr5 omega
####################
# Test renaming a nonexistent abbreviation
####################
# Test renaming to a abbreviation with spaces
####################
# Test renaming without arguments
####################
# Test renaming with too many arguments
abbr -a -U -- __abbr8 omega
####################
# Test renaming to existing abbreviation
abbr __abbr1 'alpha beta gamma'
abbr __abbr1 'alpha beta gamma'
abbr __abbr1 'alpha beta gamma'
abbr __abbr1 delta
abbr __abbr1 delta
abbr __abbr1 delta
abbr '~__abbr2' '$xyz'
abbr -- --__abbr3 xyz
abbr __abbr5 omega
abbr __abbr8 omega

View file

@ -46,7 +46,7 @@ or not string match -q '*/share/functions/abbr.fish' $x[1]
or test $x[2] != autoloaded
or test $x[3] != 1
or test $x[4] != scope-shadowing
or test $x[5] != 'Manage abbreviations using new fish 3.0 scheme.'
or test $x[5] != 'Manage abbreviations'
echo "Unexpected output for 'functions -v -D abbr': $x" >&2
end