mirror of
https://github.com/fish-shell/fish-shell.git
synced 2026-06-09 20:21:16 -03:00
Add set --function (#8145)
* Add `set --function`
This makes the function's scope available, even inside of blocks. Outside of blocks it's the toplevel local scope.
This removes the need to declare variables locally before use, and will probably end up being the main way variables get set.
E.g.:
```fish
set -l thing
if condition
set thing one
else
set thing two
end
```
could be written as
```fish
if condition
set -f thing one
else
set -f thing two
end
```
Note: Many scripts shipped with fish use workarounds like `and`/`or`
instead of `if`, so it isn't easy to find good examples.
Also, if there isn't an else-branch in that above, just with
```fish
if condition
set -f thing one
end
```
that means something different from setting it before! Now, if
`condition` isn't true, it would use a global (or universal) variable of
te same name!
Some more interesting parts:
Because it *is* a local scope, setting a variable `-f` and
`-l` in the toplevel of a function ends up the same:
```fish
function foo2
set -l foo bar
set -f foo baz # modifies the *same* variable!
end
```
but setting it locally inside a block creates a new local variable
that shadows the function-scoped variable:
```fish
function foo3
set -f foo bar
begin
set -l foo banana
# $foo is banana
end
# $foo is bar again
end
```
This is how local variables already work. "Local" is actually "block-scoped".
Also `set --show` will only show the closest local scope, so it won't
show a shadowed function-level variable. Again, this is how local
variables already work, and could be done as a separate change.
As a fun tidbit, functions with --no-scope-shadowing can now use this to set variables in the calling function. That's probably okay given that it's already an escape hatch (but to be clear: if it turns out to problematic I reserve the right to remove it).
Fixes #565
This commit is contained in:
22
src/env.cpp
22
src/env.cpp
@@ -472,8 +472,9 @@ struct query_t {
|
||||
// Whether any scopes were specified.
|
||||
bool has_scope;
|
||||
|
||||
// Whether to search local, global, universal scopes.
|
||||
// Whether to search local, function, global, universal scopes.
|
||||
bool local;
|
||||
bool function;
|
||||
bool global;
|
||||
bool universal;
|
||||
|
||||
@@ -493,8 +494,9 @@ struct query_t {
|
||||
bool user;
|
||||
|
||||
explicit query_t(env_mode_flags_t mode) {
|
||||
has_scope = mode & (ENV_LOCAL | ENV_GLOBAL | ENV_UNIVERSAL);
|
||||
has_scope = mode & (ENV_LOCAL | ENV_FUNCTION | ENV_GLOBAL | ENV_UNIVERSAL);
|
||||
local = !has_scope || (mode & ENV_LOCAL);
|
||||
function = !has_scope || (mode & ENV_FUNCTION);
|
||||
global = !has_scope || (mode & ENV_GLOBAL);
|
||||
universal = !has_scope || (mode & ENV_UNIVERSAL);
|
||||
|
||||
@@ -1190,6 +1192,22 @@ mod_result_t env_stack_impl_t::set(const wcstring &key, env_mode_flags_t mode,
|
||||
} else if (query.local) {
|
||||
assert(locals_ != globals_ && "Locals should not be globals");
|
||||
set_in_node(locals_, key, std::move(val), flags);
|
||||
} else if (query.function) {
|
||||
// "Function" scope is:
|
||||
// Either the topmost local scope of the nearest function,
|
||||
// or the top-level local scope if no function exists.
|
||||
//
|
||||
// This is distinct from the unspecified scope,
|
||||
// which is the global scope if no function exists.
|
||||
auto node = locals_;
|
||||
while (node->next) {
|
||||
node = node->next;
|
||||
// The first node that introduces a new scope is ours.
|
||||
// If this doesn't happen, we go on until we've reached the
|
||||
// topmost local scope.
|
||||
if (node->new_scope) break;
|
||||
}
|
||||
set_in_node(node, key, std::move(val), flags);
|
||||
} else {
|
||||
DIE("Unknown scope");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user