2023-02-24 15:54:42 +00:00
|
|
|
use nu_protocol::engine::{EngineState, StateWorkingSet};
|
|
|
|
|
|
|
|
use crate::*;
|
|
|
|
|
|
|
|
pub fn create_default_context() -> EngineState {
|
|
|
|
let mut engine_state = EngineState::new();
|
|
|
|
|
|
|
|
let delta = {
|
|
|
|
let mut working_set = StateWorkingSet::new(&engine_state);
|
|
|
|
|
|
|
|
macro_rules! bind_command {
|
|
|
|
( $( $command:expr ),* $(,)? ) => {
|
|
|
|
$( working_set.add_decl(Box::new($command)); )*
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
// Core
|
|
|
|
bind_command! {
|
|
|
|
Alias,
|
|
|
|
Break,
|
2023-03-24 09:50:23 +00:00
|
|
|
Collect,
|
2023-02-24 15:54:42 +00:00
|
|
|
Const,
|
|
|
|
Continue,
|
|
|
|
Def,
|
|
|
|
Describe,
|
|
|
|
Do,
|
|
|
|
Echo,
|
|
|
|
ErrorMake,
|
|
|
|
ExportAlias,
|
|
|
|
ExportCommand,
|
Module: support defining const and use const variables inside of function (#9773)
<!--
if this PR closes one or more issues, you can automatically link the PR
with
them by using one of the [*linking
keywords*](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword),
e.g.
- this PR should close #xxxx
- fixes #xxxx
you can also mention related issues, PRs or discussions!
-->
# Description
<!--
Thank you for improving Nushell. Please, check our [contributing
guide](../CONTRIBUTING.md) and talk to the core team before making major
changes.
Description of your pull request goes here. **Provide examples and/or
screenshots** if your changes affect the user experience.
-->
Relative: #8248
After this pr, user can define const variable inside a module.
![image](https://github.com/nushell/nushell/assets/22256154/e3e03e56-c4b5-4144-a944-d1b20bec1cbd)
And user can export const variables, the following screenshot shows how
it works (it follows
https://github.com/nushell/nushell/issues/8248#issuecomment-1637442612):
![image](https://github.com/nushell/nushell/assets/22256154/b2c14760-3f27-41cc-af77-af70a4367f2a)
## About the change
1. To make module support const, we need to change `parse_module_block`
to support `const` keyword.
2. To suport export `const`, we need to make module tracking variables,
so we add `variables` attribute to `Module`
3. During eval, the const variable may not exists in `stack`, because we
don't eval `const` when we define a module, so we need to find variables
which are already registered in `engine_state`
## One more thing to note about the const value.
Consider the following code
```
module foo { const b = 3; export def bar [] { $b } }
use foo bar
const b = 4;
bar
```
The result will be 3 (which is defined in module) rather than 4. I think
it's expected behavior.
It's something like [dynamic
binding](https://www.gnu.org/software/emacs/manual/html_node/elisp/Dynamic-Binding-Tips.html)
vs [lexical
binding](https://www.gnu.org/software/emacs/manual/html_node/elisp/Lexical-Binding.html)
in lisp like language, and lexical binding should be right behavior
which generates more predicable result, and it doesn't introduce really
subtle bugs in nushell code.
What if user want dynamic-binding?(For example: the example code returns
`4`)
There is no way to do this, user should consider passing the value as
argument to custom command rather than const.
## TODO
- [X] adding tests for the feature.
- [X] support export const out of module to use.
# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->
# Tests + Formatting
<!--
Don't forget to add tests that cover your changes.
Make sure you've run and fixed any issues with these commands:
- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect -A clippy::result_large_err` to check that
you're using the standard code style
- `cargo test --workspace` to check that all tests pass
- `cargo run -- -c "use std testing; testing run-tests --path
crates/nu-std"` to run the tests for the standard library
> **Note**
> from `nushell` you can also use the `toolkit` as follows
> ```bash
> use toolkit.nu # or use an `env_change` hook to activate it
automatically
> toolkit check pr
> ```
-->
# After Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->
2023-07-31 23:09:52 +00:00
|
|
|
ExportConst,
|
2023-02-24 15:54:42 +00:00
|
|
|
ExportDef,
|
|
|
|
ExportExtern,
|
|
|
|
ExportUse,
|
2023-05-06 18:39:54 +00:00
|
|
|
ExportModule,
|
2023-02-24 15:54:42 +00:00
|
|
|
Extern,
|
|
|
|
For,
|
|
|
|
Hide,
|
|
|
|
HideEnv,
|
|
|
|
If,
|
|
|
|
Ignore,
|
|
|
|
Overlay,
|
|
|
|
OverlayUse,
|
|
|
|
OverlayList,
|
|
|
|
OverlayNew,
|
|
|
|
OverlayHide,
|
Feature: Userland LazyRecords (#8332)
# Description
Despite the innocent-looking title, this PR involves quite a few backend
changes as the existing LazyRecord trait was not at all friendly towards
the idea of these values being generated on the fly from Nu code.
In particular, here are a few changes involved:
- The LazyRecord trait now involves a lifetime `'a`, and this lifetime
is used in the return value of `get_column_names`. This means it no
longer returns `'static str`s (but implementations still can return
these). This is more stringent on the consumption side.
- The LazyRecord trait now must be able to clone itself via a new
`clone_value` method (as requiring `Clone` is not object safe). This
pattern is borrowed from `Value::CustomValue`.
- LazyRecord no longer requires being serde serializable and
deserializable.
These, in hand, allow for the following:
- LazyRecord can now clone itself, which means that they don't have to
be collected into a Record when being cloned.
- This is especially useful in Stack, which is cloned on each repl line
and in a few other cases. This would mean that _every_ LazyRecord
instance stored in a variable would be collected in its entirety and
cloned, which can be catastrophic for performance. See: `let nulol =
$nu`.
- LazyRecord's columns don't have to be static, they can have the same
lifetime of the struct itself, so different instances of the same
LazyRecord type can have different columns and values (like the new
`NuLazyRecord`)
- Serialization and deserialization are no longer meaningless, they are
simply less.
I would consider this PR very "drafty", but everything works. It
probably requires some cleanup and testing, though, but I'd like some
eyes and pointers first.
# User-Facing Changes
New command. New restrictions are largely internal. Maybe there are some
plugins affected?
Example of new command's usage:
```
lazy make --columns [a b c] --get-value { |name| print $"getting ($name)"; $name | str upcase }
```
You can also trivially implement something like `lazy make record` to
take a record of closures and turn it into a getter-like lazy struct:
```
def "lazy make record" [
record: record
] {
let columns = ($record | columns)
lazy make --columns $columns --get-value { |col| do ($record | get $col) }
}
```
Open to bikeshedding. `lazy make` is similar to `error make` which is
also in the core commands. I didn't like `make lazy` since it sounded
like some transformation was going on.
# Tour for reviewers
Take a look at LazyMake's examples. They have `None` as the results, as
such they aren't _really_ correct and aren't being tested at all. I
didn't do this because creating the Value::LazyRecord is a little tricky
and didn't want to risk messing it up, especially as the necessary
variables aren't available when creating the examples (like stack and
engine state).
Also take a look at NuLazyRecord's get_value implementation, or in
general. It uses an Arc<Mutex<_>> for the stack, which must be accessed
mutably for eval_block but get_value only provides us with a `&self`.
This is a sad state of affairs, but I don't know if there's a better
way.
On the same code path, we also have pipeline handling, and any pipeline
that isn't a Pipeline::Value will return Value::nothing. I believe
returning a Value::Error is probably better, or maybe some other
handling. Couldn't decide on which ShellError to settle with for that
branch.
The "unfortunate casualty" in the columns.rs file. I'm not sure just how
bad that is, though, I simply had to fight a little with the borrow
checker.
A few leftover comments like derives, comments about the now
non-existing serde requirements, and impls. I'll definitely get around
to those eventually but they're in atm
Should NuLazyRecord implement caching? I'm leaning heavily towards
**yes**, this was one of the main reasons not to use a record of
closures (besides convenience), but maybe it could be opt-out. I'd
wonder about its implementation too, but a simple way would be to move a
HashMap into the mutex state and keep cached values there.
2023-05-17 23:35:22 +00:00
|
|
|
LazyMake,
|
2023-02-24 15:54:42 +00:00
|
|
|
Let,
|
|
|
|
Loop,
|
2023-03-24 01:52:01 +00:00
|
|
|
Match,
|
2023-02-24 15:54:42 +00:00
|
|
|
Module,
|
|
|
|
Mut,
|
|
|
|
Return,
|
2023-06-20 21:33:01 +00:00
|
|
|
Scope,
|
|
|
|
ScopeAliases,
|
|
|
|
ScopeCommands,
|
|
|
|
ScopeEngineStats,
|
2023-08-17 08:58:38 +00:00
|
|
|
ScopeExterns,
|
2023-06-20 21:33:01 +00:00
|
|
|
ScopeModules,
|
|
|
|
ScopeVariables,
|
2023-02-24 15:54:42 +00:00
|
|
|
Try,
|
|
|
|
Use,
|
|
|
|
Version,
|
|
|
|
While,
|
|
|
|
};
|
|
|
|
|
|
|
|
//#[cfg(feature = "plugin")]
|
Keep plugins persistently running in the background (#12064)
# Description
This PR uses the new plugin protocol to intelligently keep plugin
processes running in the background for further plugin calls.
Running plugins can be seen by running the new `plugin list` command,
and stopped by running the new `plugin stop` command.
This is an enhancement for the performance of plugins, as starting new
plugin processes has overhead, especially for plugins in languages that
take a significant amount of time on startup. It also enables plugins
that have persistent state between commands, making the migration of
features like dataframes and `stor` to plugins possible.
Plugins are automatically stopped by the new plugin garbage collector,
configurable with `$env.config.plugin_gc`:
```nushell
$env.config.plugin_gc = {
# Configuration for plugin garbage collection
default: {
enabled: true # true to enable stopping of inactive plugins
stop_after: 10sec # how long to wait after a plugin is inactive to stop it
}
plugins: {
# alternate configuration for specific plugins, by name, for example:
#
# gstat: {
# enabled: false
# }
}
}
```
If garbage collection is enabled, plugins will be stopped after
`stop_after` passes after they were last active. Plugins are counted as
inactive if they have no running plugin calls. Reading the stream from
the response of a plugin call is still considered to be activity, but if
a plugin holds on to a stream but the call ends without an active
streaming response, it is not counted as active even if it is reading
it. Plugins can explicitly disable the GC as appropriate with
`engine.set_gc_disabled(true)`.
The `version` command now lists plugin names rather than plugin
commands. The list of plugin commands is accessible via `plugin list`.
Recommend doing this together with #12029, because it will likely force
plugin developers to do the right thing with mutability and lead to less
unexpected behavior when running plugins nested / in parallel.
# User-Facing Changes
- new command: `plugin list`
- new command: `plugin stop`
- changed command: `version` (now lists plugin names, rather than
commands)
- new config: `$env.config.plugin_gc`
- Plugins will keep running and be reused, at least for the configured
GC period
- Plugins that used mutable state in weird ways like `inc` did might
misbehave until fixed
- Plugins can disable GC if they need to
- Had to change plugin signature to accept `&EngineInterface` so that
the GC disable feature works. #12029 does this anyway, and I'm expecting
(resolvable) conflicts with that
# Tests + Formatting
- :green_circle: `toolkit fmt`
- :green_circle: `toolkit clippy`
- :green_circle: `toolkit test`
- :green_circle: `toolkit test stdlib`
Because there is some specific OS behavior required for plugins to not
respond to Ctrl-C directly, I've developed against and tested on both
Linux and Windows to ensure that works properly.
# After Submitting
I think this probably needs to be in the book somewhere
2024-03-09 23:10:22 +00:00
|
|
|
bind_command!(PluginCommand, PluginList, PluginStop, Register,);
|
2023-02-24 15:54:42 +00:00
|
|
|
|
|
|
|
working_set.render()
|
|
|
|
};
|
|
|
|
|
|
|
|
if let Err(err) = engine_state.merge_delta(delta) {
|
|
|
|
eprintln!("Error creating default context: {err:?}");
|
|
|
|
}
|
|
|
|
|
|
|
|
engine_state
|
|
|
|
}
|