mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-25 20:33:08 +00:00
Add new functions
flag -V/--inherit-variable
--inherit-variable takes a variable name and snapshots its current value. When the function is executed, it will have a local variable with this value already defined. Printing the function source will include synthesized `set -l` lines for the values. This is primarily useful for functions that are created on the fly, such as in `psub`.
This commit is contained in:
parent
6d7a7b00d7
commit
cfc06203e7
10 changed files with 152 additions and 7 deletions
38
builtin.cpp
38
builtin.cpp
|
@ -1391,6 +1391,26 @@ static void functions_def(const wcstring &name, wcstring &out)
|
||||||
out.append(escape_string(name, true));
|
out.append(escape_string(name, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Output any inherited variables as `set -l` lines */
|
||||||
|
std::map<wcstring,env_var_t> inherit_vars = function_get_inherit_vars(name);
|
||||||
|
for (std::map<wcstring,env_var_t>::const_iterator it = inherit_vars.begin(), end = inherit_vars.end(); it != end; ++it)
|
||||||
|
{
|
||||||
|
wcstring_list_t lst;
|
||||||
|
if (!it->second.missing())
|
||||||
|
{
|
||||||
|
tokenize_variable_array(it->second, lst);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This forced tab is crummy, but we don't know what indentation style the function uses */
|
||||||
|
append_format(out, L"\n\tset -l %ls", it->first.c_str());
|
||||||
|
for (wcstring_list_t::const_iterator arg_it = lst.begin(), arg_end = lst.end(); arg_it != arg_end; ++arg_it)
|
||||||
|
{
|
||||||
|
wcstring earg = escape_string(*arg_it, ESCAPE_ALL);
|
||||||
|
out.push_back(L' ');
|
||||||
|
out.append(earg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* This forced tab is sort of crummy - not all functions start with a tab */
|
/* This forced tab is sort of crummy - not all functions start with a tab */
|
||||||
append_format(out, L"\n\t%ls", def.c_str());
|
append_format(out, L"\n\t%ls", def.c_str());
|
||||||
|
|
||||||
|
@ -1976,6 +1996,7 @@ int define_function(parser_t &parser, const wcstring_list_t &c_args, const wcstr
|
||||||
wchar_t *desc=0;
|
wchar_t *desc=0;
|
||||||
std::vector<event_t> events;
|
std::vector<event_t> events;
|
||||||
std::auto_ptr<wcstring_list_t> named_arguments(NULL);
|
std::auto_ptr<wcstring_list_t> named_arguments(NULL);
|
||||||
|
wcstring_list_t inherit_vars;
|
||||||
|
|
||||||
wchar_t *name = 0;
|
wchar_t *name = 0;
|
||||||
bool shadows = true;
|
bool shadows = true;
|
||||||
|
@ -1996,6 +2017,7 @@ int define_function(parser_t &parser, const wcstring_list_t &c_args, const wcstr
|
||||||
{ L"help", no_argument, 0, 'h' },
|
{ L"help", no_argument, 0, 'h' },
|
||||||
{ L"argument-names", no_argument, 0, 'a' },
|
{ L"argument-names", no_argument, 0, 'a' },
|
||||||
{ L"no-scope-shadowing", no_argument, 0, 'S' },
|
{ L"no-scope-shadowing", no_argument, 0, 'S' },
|
||||||
|
{ L"inherit-variable", required_argument, 0, 'V' },
|
||||||
{ 0, 0, 0, 0 }
|
{ 0, 0, 0, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2005,7 +2027,7 @@ int define_function(parser_t &parser, const wcstring_list_t &c_args, const wcstr
|
||||||
|
|
||||||
int opt = wgetopt_long(argc,
|
int opt = wgetopt_long(argc,
|
||||||
argv,
|
argv,
|
||||||
L"d:s:j:p:v:e:haS",
|
L"d:s:j:p:v:e:haSV:",
|
||||||
long_options,
|
long_options,
|
||||||
&opt_index);
|
&opt_index);
|
||||||
if (opt == -1)
|
if (opt == -1)
|
||||||
|
@ -2159,6 +2181,19 @@ int define_function(parser_t &parser, const wcstring_list_t &c_args, const wcstr
|
||||||
wrap_targets.push_back(woptarg);
|
wrap_targets.push_back(woptarg);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'V':
|
||||||
|
{
|
||||||
|
if (wcsvarname(woptarg))
|
||||||
|
{
|
||||||
|
append_format(*out_err, _(L"%ls: Invalid variable name '%ls'\n"), argv[0], woptarg);
|
||||||
|
res = STATUS_BUILTIN_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
inherit_vars.push_back(woptarg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case 'h':
|
case 'h':
|
||||||
builtin_print_help(parser, argv[0], stdout_buffer);
|
builtin_print_help(parser, argv[0], stdout_buffer);
|
||||||
return STATUS_BUILTIN_OK;
|
return STATUS_BUILTIN_OK;
|
||||||
|
@ -2255,6 +2290,7 @@ int define_function(parser_t &parser, const wcstring_list_t &c_args, const wcstr
|
||||||
d.shadows = shadows;
|
d.shadows = shadows;
|
||||||
if (named_arguments.get())
|
if (named_arguments.get())
|
||||||
d.named_arguments.swap(*named_arguments);
|
d.named_arguments.swap(*named_arguments);
|
||||||
|
d.inherit_vars.swap(inherit_vars);
|
||||||
|
|
||||||
for (size_t i=0; i<d.events.size(); i++)
|
for (size_t i=0; i<d.events.size(); i++)
|
||||||
{
|
{
|
||||||
|
|
|
@ -21,7 +21,9 @@ The following options are available:
|
||||||
|
|
||||||
- `-e` or `--on-event EVENT_NAME` tells fish to run this function when the specified named event is emitted. Fish internally generates named events e.g. when showing the prompt.
|
- `-e` or `--on-event EVENT_NAME` tells fish to run this function when the specified named event is emitted. Fish internally generates named events e.g. when showing the prompt.
|
||||||
|
|
||||||
- `-j PID` or `--on-job-exit PID` tells fish to run this function when the job with group ID PID exits. Instead of PID, the string 'caller' can be specified. This is only legal when in a command substitution, and will result in the handler being triggered by the exit of the job which created this command substitution.
|
- `-v` or `--on-variable VARIABLE_NAME` tells fish to run this function when the variable VARIABLE_NAME changes value.
|
||||||
|
|
||||||
|
- `-j PGID` or `--on-job-exit PGID` tells fish to run this function when the job with group ID PGID exits. Instead of PGID, the string 'caller' can be specified. This is only legal when in a command substitution, and will result in the handler being triggered by the exit of the job which created this command substitution.
|
||||||
|
|
||||||
- `-p PID` or `--on-process-exit PID` tells fish to run this function when the fish child process with process ID PID exits.
|
- `-p PID` or `--on-process-exit PID` tells fish to run this function when the fish child process with process ID PID exits.
|
||||||
|
|
||||||
|
@ -29,7 +31,7 @@ The following options are available:
|
||||||
|
|
||||||
- `-S` or `--no-scope-shadowing` allows the function to access the variables of calling functions. Normally, any variables inside the function that have the same name as variables from the calling function are "shadowed", and their contents is independent of the calling function.
|
- `-S` or `--no-scope-shadowing` allows the function to access the variables of calling functions. Normally, any variables inside the function that have the same name as variables from the calling function are "shadowed", and their contents is independent of the calling function.
|
||||||
|
|
||||||
- `-v` or `--on-variable VARIABLE_NAME` tells fish to run this function when the variable VARIABLE_NAME changes value.
|
- `-V` or `--inherit-variable NAME` snapshots the value of the variable `NAME` and defines a local variable with that same name and value when the function is executed.
|
||||||
|
|
||||||
If the user enters any additional arguments after the function, they are inserted into the environment <a href="index.html#variables-arrays">variable array</a> `$argv`. If the `--argument-names` option is provided, the arguments are also assigned to names specified in that option.
|
If the user enters any additional arguments after the function, they are inserted into the environment <a href="index.html#variables-arrays">variable array</a> `$argv`. If the `--argument-names` option is provided, the arguments are also assigned to names specified in that option.
|
||||||
|
|
||||||
|
@ -75,3 +77,17 @@ end
|
||||||
|
|
||||||
This will run the `mkdir` command, and if it is successful, change the current working directory to the one just created.
|
This will run the `mkdir` command, and if it is successful, change the current working directory to the one just created.
|
||||||
|
|
||||||
|
\fish
|
||||||
|
function notify
|
||||||
|
set -l job (jobs -l -g)
|
||||||
|
or begin; echo "There are no jobs" >&2; return 1; end
|
||||||
|
|
||||||
|
function _notify_job_$job --on-job-exit $job --inherit-variable job
|
||||||
|
echo -n \a # beep
|
||||||
|
functions -e _notify_job_$job
|
||||||
|
end
|
||||||
|
end
|
||||||
|
\endfish
|
||||||
|
|
||||||
|
This will beep when the most recent job completes.
|
||||||
|
|
||||||
|
|
2
env.h
2
env.h
|
@ -166,7 +166,7 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Gets the variable with the specified name, or env_var_t::missing_var if it does not exist.
|
Gets the variable with the specified name, or env_var_t::missing_var if it does not exist or is an empty array.
|
||||||
|
|
||||||
\param key The name of the variable to get
|
\param key The name of the variable to get
|
||||||
\param mode An optional scope to search in. All scopes are searched if unset
|
\param mode An optional scope to search in. All scopes are searched if unset
|
||||||
|
|
7
exec.cpp
7
exec.cpp
|
@ -856,6 +856,7 @@ void exec_job(parser_t &parser, job_t *j)
|
||||||
|
|
||||||
wcstring_list_t named_arguments = function_get_named_arguments(p->argv0());
|
wcstring_list_t named_arguments = function_get_named_arguments(p->argv0());
|
||||||
bool shadows = function_get_shadows(p->argv0());
|
bool shadows = function_get_shadows(p->argv0());
|
||||||
|
std::map<wcstring,env_var_t> inherit_vars = function_get_inherit_vars(p->argv0());
|
||||||
|
|
||||||
signal_block();
|
signal_block();
|
||||||
|
|
||||||
|
@ -868,12 +869,16 @@ void exec_job(parser_t &parser, job_t *j)
|
||||||
parser.push_block(newv);
|
parser.push_block(newv);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
set_argv might trigger an event
|
setting variables might trigger an event
|
||||||
handler, hence we need to unblock
|
handler, hence we need to unblock
|
||||||
signals.
|
signals.
|
||||||
*/
|
*/
|
||||||
signal_unblock();
|
signal_unblock();
|
||||||
parse_util_set_argv(p->get_argv()+1, named_arguments);
|
parse_util_set_argv(p->get_argv()+1, named_arguments);
|
||||||
|
for (std::map<wcstring,env_var_t>::const_iterator it = inherit_vars.begin(), end = inherit_vars.end(); it != end; ++it)
|
||||||
|
{
|
||||||
|
env_set(it->first, it->second.missing() ? NULL : it->second.c_str(), ENV_LOCAL | ENV_USER);
|
||||||
|
}
|
||||||
signal_block();
|
signal_block();
|
||||||
|
|
||||||
parser.forbid_function(p->argv0());
|
parser.forbid_function(p->argv0());
|
||||||
|
|
19
function.cpp
19
function.cpp
|
@ -150,12 +150,23 @@ void function_init()
|
||||||
VOMIT_ON_FAILURE(pthread_mutexattr_destroy(&a));
|
VOMIT_ON_FAILURE(pthread_mutexattr_destroy(&a));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::map<wcstring,env_var_t> snapshot_vars(const wcstring_list_t &vars)
|
||||||
|
{
|
||||||
|
std::map<wcstring,env_var_t> result;
|
||||||
|
for (wcstring_list_t::const_iterator it = vars.begin(), end = vars.end(); it != end; ++it)
|
||||||
|
{
|
||||||
|
result.insert(std::make_pair(*it, env_get_string(*it)));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
function_info_t::function_info_t(const function_data_t &data, const wchar_t *filename, int def_offset, bool autoload) :
|
function_info_t::function_info_t(const function_data_t &data, const wchar_t *filename, int def_offset, bool autoload) :
|
||||||
definition(data.definition),
|
definition(data.definition),
|
||||||
description(data.description),
|
description(data.description),
|
||||||
definition_file(intern(filename)),
|
definition_file(intern(filename)),
|
||||||
definition_offset(def_offset),
|
definition_offset(def_offset),
|
||||||
named_arguments(data.named_arguments),
|
named_arguments(data.named_arguments),
|
||||||
|
inherit_vars(snapshot_vars(data.inherit_vars)),
|
||||||
is_autoload(autoload),
|
is_autoload(autoload),
|
||||||
shadows(data.shadows)
|
shadows(data.shadows)
|
||||||
{
|
{
|
||||||
|
@ -167,6 +178,7 @@ function_info_t::function_info_t(const function_info_t &data, const wchar_t *fil
|
||||||
definition_file(intern(filename)),
|
definition_file(intern(filename)),
|
||||||
definition_offset(def_offset),
|
definition_offset(def_offset),
|
||||||
named_arguments(data.named_arguments),
|
named_arguments(data.named_arguments),
|
||||||
|
inherit_vars(data.inherit_vars),
|
||||||
is_autoload(autoload),
|
is_autoload(autoload),
|
||||||
shadows(data.shadows)
|
shadows(data.shadows)
|
||||||
{
|
{
|
||||||
|
@ -268,6 +280,13 @@ wcstring_list_t function_get_named_arguments(const wcstring &name)
|
||||||
return func ? func->named_arguments : wcstring_list_t();
|
return func ? func->named_arguments : wcstring_list_t();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::map<wcstring,env_var_t> function_get_inherit_vars(const wcstring &name)
|
||||||
|
{
|
||||||
|
scoped_lock lock(functions_lock);
|
||||||
|
const function_info_t *func = function_get(name);
|
||||||
|
return func ? func->inherit_vars : std::map<wcstring,env_var_t>();
|
||||||
|
}
|
||||||
|
|
||||||
int function_get_shadows(const wcstring &name)
|
int function_get_shadows(const wcstring &name)
|
||||||
{
|
{
|
||||||
scoped_lock lock(functions_lock);
|
scoped_lock lock(functions_lock);
|
||||||
|
|
16
function.h
16
function.h
|
@ -11,10 +11,12 @@
|
||||||
#define FISH_FUNCTION_H
|
#define FISH_FUNCTION_H
|
||||||
|
|
||||||
#include <wchar.h>
|
#include <wchar.h>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "event.h"
|
#include "event.h"
|
||||||
|
#include "env.h"
|
||||||
|
|
||||||
class parser_t;
|
class parser_t;
|
||||||
class env_vars_snapshot_t;
|
class env_vars_snapshot_t;
|
||||||
|
@ -48,6 +50,11 @@ struct function_data_t
|
||||||
List of all named arguments for this function
|
List of all named arguments for this function
|
||||||
*/
|
*/
|
||||||
wcstring_list_t named_arguments;
|
wcstring_list_t named_arguments;
|
||||||
|
/**
|
||||||
|
List of all variables that are inherited from the function definition scope.
|
||||||
|
The variable values are snapshotted when function_add() is called.
|
||||||
|
*/
|
||||||
|
wcstring_list_t inherit_vars;
|
||||||
/**
|
/**
|
||||||
Set to non-zero if invoking this function shadows the variables
|
Set to non-zero if invoking this function shadows the variables
|
||||||
of the underlying function.
|
of the underlying function.
|
||||||
|
@ -79,6 +86,9 @@ public:
|
||||||
/** List of all named arguments for this function */
|
/** List of all named arguments for this function */
|
||||||
const wcstring_list_t named_arguments;
|
const wcstring_list_t named_arguments;
|
||||||
|
|
||||||
|
/** Mapping of all variables that were inherited from the function definition scope to their values */
|
||||||
|
const std::map<wcstring,env_var_t> inherit_vars;
|
||||||
|
|
||||||
/** Flag for specifying that this function was automatically loaded */
|
/** Flag for specifying that this function was automatically loaded */
|
||||||
const bool is_autoload;
|
const bool is_autoload;
|
||||||
|
|
||||||
|
@ -162,6 +172,12 @@ int function_get_definition_offset(const wcstring &name);
|
||||||
*/
|
*/
|
||||||
wcstring_list_t function_get_named_arguments(const wcstring &name);
|
wcstring_list_t function_get_named_arguments(const wcstring &name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Returns a mapping of all variables of the specified function that were inherited
|
||||||
|
from the scope of the function definition to their values.
|
||||||
|
*/
|
||||||
|
std::map<wcstring,env_var_t> function_get_inherit_vars(const wcstring &name);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Creates a new function using the same definition as the specified function.
|
Creates a new function using the same definition as the specified function.
|
||||||
Returns true if copy is successful.
|
Returns true if copy is successful.
|
||||||
|
|
0
tests/function.err
Normal file
0
tests/function.err
Normal file
32
tests/function.in
Normal file
32
tests/function.in
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
# vim: set filetype=fish:
|
||||||
|
#
|
||||||
|
# Test the `function` builtin
|
||||||
|
|
||||||
|
# utility function
|
||||||
|
function show_ary -a name --no-scope-shadowing
|
||||||
|
set -l count (count $$name)
|
||||||
|
echo "\$$name: ($count)"
|
||||||
|
if test $count -gt 0
|
||||||
|
for i in (seq $count)
|
||||||
|
echo "$i: '$$name[1][$i]'"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Test the -V flag
|
||||||
|
set -g foo 'global foo'
|
||||||
|
set -l foo 'local foo'
|
||||||
|
set bar one 'two 2' \t '' 3
|
||||||
|
set baz
|
||||||
|
function frob -V foo -V bar -V baz
|
||||||
|
show_ary foo
|
||||||
|
show_ary bar
|
||||||
|
show_ary baz
|
||||||
|
end
|
||||||
|
echo "Testing -V"
|
||||||
|
frob
|
||||||
|
echo "Testing -V with changed variables"
|
||||||
|
set foo 'bad foo'
|
||||||
|
set bar 'bad bar'
|
||||||
|
set baz 'bad baz'
|
||||||
|
frob
|
20
tests/function.out
Normal file
20
tests/function.out
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
Testing -V
|
||||||
|
$foo: (1)
|
||||||
|
1: 'local foo'
|
||||||
|
$bar: (5)
|
||||||
|
1: 'one'
|
||||||
|
2: 'two 2'
|
||||||
|
3: ' '
|
||||||
|
4: ''
|
||||||
|
5: '3'
|
||||||
|
$baz: (0)
|
||||||
|
Testing -V with changed variables
|
||||||
|
$foo: (1)
|
||||||
|
1: 'local foo'
|
||||||
|
$bar: (5)
|
||||||
|
1: 'one'
|
||||||
|
2: 'two 2'
|
||||||
|
3: ' '
|
||||||
|
4: ''
|
||||||
|
5: '3'
|
||||||
|
$baz: (0)
|
1
tests/function.status
Normal file
1
tests/function.status
Normal file
|
@ -0,0 +1 @@
|
||||||
|
0
|
Loading…
Reference in a new issue