nushell/crates/nu-cmd-lang/src/default_context.rs

79 lines
1.7 KiB
Rust
Raw Normal View History

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,
Collect,
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,
ExportDef,
ExportExtern,
ExportUse,
ExportModule,
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,
Let,
Loop,
Add pattern matching (#8590) # Description This adds `match` and basic pattern matching. An example: ``` match $x { 1..10 => { print "Value is between 1 and 10" } { foo: $bar } => { print $"Value has a 'foo' field with value ($bar)" } [$a, $b] => { print $"Value is a list with two items: ($a) and ($b)" } _ => { print "Value is none of the above" } } ``` Like the recent changes to `if` to allow it to be used as an expression, `match` can also be used as an expression. This allows you to assign the result to a variable, eg) `let xyz = match ...` I've also included a short-hand pattern for matching records, as I think it might help when doing a lot of record patterns: `{$foo}` which is equivalent to `{foo: $foo}`. There are still missing components, so consider this the first step in full pattern matching support. Currently missing: * Patterns for strings * Or-patterns (like the `|` in Rust) * Patterns for tables (unclear how we want to match a table, so it'll need some design) * Patterns for binary values * And much more # User-Facing Changes [see above] # 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` to check that you're using the standard code style - `cargo test --workspace` to check that all tests pass > **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-03-24 01:52:01 +00:00
Match,
Module,
Mut,
Return,
Scope,
ScopeAliases,
ScopeCommands,
ScopeEngineStats,
2023-08-17 08:58:38 +00:00
ScopeExterns,
ScopeModules,
ScopeVariables,
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,);
working_set.render()
};
if let Err(err) = engine_state.merge_delta(delta) {
eprintln!("Error creating default context: {err:?}");
}
engine_state
}