2022-01-18 20:32:45 +01:00
|
|
|
use crate::{
|
2022-05-05 17:10:03 +02:00
|
|
|
completions::NuCompleter,
|
|
|
|
prompt_update,
|
|
|
|
reedline_config::{add_menus, create_keybindings, KeybindingsMode},
|
2023-03-20 05:05:22 +01:00
|
|
|
util::eval_source,
|
2022-05-05 17:10:03 +02:00
|
|
|
NuHighlighter, NuValidator, NushellPrompt,
|
2022-01-18 20:32:45 +01:00
|
|
|
};
|
2023-04-14 22:14:57 +02:00
|
|
|
use crossterm::cursor::SetCursorStyle;
|
2023-01-24 21:28:59 +01:00
|
|
|
use log::{trace, warn};
|
2023-07-07 03:16:17 +02:00
|
|
|
use miette::{ErrReport, IntoDiagnostic, Result};
|
2023-08-29 23:46:50 +02:00
|
|
|
use nu_cmd_base::hook::eval_hook;
|
2023-06-23 21:23:08 +02:00
|
|
|
use nu_cmd_base::util::get_guaranteed_cwd;
|
color_config now accepts closures as color values (#7141)
# Description
Closes #6909. You can now add closures to your `color_config` themes.
Whenever a value would be printed with `table`, the closure is run with
the value piped-in. The closure must return either a {fg,bg,attr} record
or a color name (`'light_red'` etc.). This returned style is used to
colour the value.
This is entirely backwards-compatible with existing config.nu files.
Example code excerpt:
```
let my_theme = {
header: green_bold
bool: { if $in { 'light_cyan' } else { 'light_red' } }
int: purple_bold
filesize: { |e| if $e == 0b { 'gray' } else if $e < 1mb { 'purple_bold' } else { 'cyan_bold' } }
duration: purple_bold
date: { (date now) - $in | if $in > 1wk { 'cyan_bold' } else if $in > 1day { 'green_bold' } else { 'yellow_bold' } }
range: yellow_bold
string: { if $in =~ '^#\w{6}$' { $in } else { 'white' } }
nothing: white
```
Example output with this in effect:
![2022-11-16 12 47 23 AM - style_computer
rs_-_nushell_-_VSCodium](https://user-images.githubusercontent.com/83939/201952558-482de05d-69c7-4bf2-91fc-d0964bf71264.png)
![2022-11-16 12 39 41 AM - style_computer
rs_-_nushell_-_VSCodium](https://user-images.githubusercontent.com/83939/201952580-2384bb86-b680-40fe-8192-71bae396c738.png)
![2022-11-15 09 21 54 PM - run_external
rs_-_nushell_-_VSCodium](https://user-images.githubusercontent.com/83939/201952601-343fc15d-e4a8-4a92-ad89-9a7d17d42748.png)
Slightly important notes:
* Some color_config names, namely "separator", "empty" and "hints", pipe
in `null` instead of a value.
* Currently, doing anything non-trivial inside a closure has an
understandably big perf hit. I currently do not actually recommend
something like `string: { if $in =~ '^#\w{6}$' { $in } else { 'white' }
}` for serious work, mainly because of the abundance of string-type data
in the world. Nevertheless, lesser-used types like "date" and "duration"
work well with this.
* I had to do some reorganisation in order to make it possible to call
`eval_block()` that late in table rendering. I invented a new struct
called "StyleComputer" which holds the engine_state and stack of the
initial `table` command (implicit or explicit).
* StyleComputer has a `compute()` method which takes a color_config name
and a nu value, and always returns the correct Style, so you don't have
to worry about A) the color_config value was set at all, B) whether it
was set to a closure or not, or C) which default style to use in those
cases.
* Currently, errors encountered during execution of the closures are
thrown in the garbage. Any other ideas are welcome. (Nonetheless, errors
result in a huge perf hit when they are encountered. I think what should
be done is to assume something terrible happened to the user's config
and invalidate the StyleComputer for that `table` run, thus causing
subsequent output to just be Style::default().)
* More thorough tests are forthcoming - ran into some difficulty using
`nu!` to take an alternative config, and for some reason `let-env config
=` statements don't seem to work inside `nu!` pipelines(???)
* The default config.nu has not been updated to make use of this yet. Do
tell if you think I should incorporate that into this.
# 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 --features=extra -- -D warnings -D
clippy::unwrap_used -A clippy::needless_collect` to check that you're
using the standard code style
- `cargo test --workspace --features=extra` to check that all tests pass
# 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.
2022-12-17 14:07:56 +01:00
|
|
|
use nu_color_config::StyleComputer;
|
2023-05-10 14:05:01 +02:00
|
|
|
use nu_engine::convert_env_values;
|
2023-05-30 16:38:45 +02:00
|
|
|
use nu_parser::{lex, parse, trim_quotes_str};
|
2022-01-18 09:48:28 +01:00
|
|
|
use nu_protocol::{
|
2023-01-13 21:37:39 +01:00
|
|
|
config::NuCursorShape,
|
2023-03-16 23:45:35 +01:00
|
|
|
engine::{EngineState, Stack, StateWorkingSet},
|
2023-09-01 08:18:55 +02:00
|
|
|
eval_const::create_nu_constant,
|
2023-05-10 14:05:01 +02:00
|
|
|
report_error, report_error_new, HistoryFileFormat, PipelineData, ShellError, Span, Spanned,
|
2023-09-01 08:18:55 +02:00
|
|
|
Value, NU_VARIABLE_ID,
|
2022-01-18 09:48:28 +01:00
|
|
|
};
|
2023-01-24 21:28:59 +01:00
|
|
|
use nu_utils::utils::perf;
|
2023-07-07 03:16:17 +02:00
|
|
|
use reedline::{
|
|
|
|
CursorConfig, DefaultHinter, EditCommand, Emacs, FileBackedHistory, HistorySessionId, Reedline,
|
|
|
|
SqliteBackedHistory, Vi,
|
|
|
|
};
|
2022-09-19 16:28:36 +02:00
|
|
|
use std::{
|
2023-08-25 10:54:44 +02:00
|
|
|
io::{self, IsTerminal, Write},
|
2023-07-07 03:16:17 +02:00
|
|
|
path::Path,
|
2022-09-19 16:28:36 +02:00
|
|
|
sync::atomic::Ordering,
|
|
|
|
time::Instant,
|
|
|
|
};
|
2022-06-14 22:53:33 +02:00
|
|
|
use sysinfo::SystemExt;
|
2022-01-18 09:48:28 +01:00
|
|
|
|
2022-07-20 22:03:29 +02:00
|
|
|
// According to Daniel Imms @Tyriar, we need to do these this way:
|
|
|
|
// <133 A><prompt><133 B><command><133 C><command output>
|
|
|
|
// These first two have been moved to prompt_update to get as close as possible to the prompt.
|
|
|
|
// const PRE_PROMPT_MARKER: &str = "\x1b]133;A\x1b\\";
|
|
|
|
// const POST_PROMPT_MARKER: &str = "\x1b]133;B\x1b\\";
|
2022-06-03 00:57:19 +02:00
|
|
|
const PRE_EXECUTE_MARKER: &str = "\x1b]133;C\x1b\\";
|
2022-07-20 22:03:29 +02:00
|
|
|
// This one is in get_command_finished_marker() now so we can capture the exit codes properly.
|
|
|
|
// const CMD_FINISHED_MARKER: &str = "\x1b]133;D;{}\x1b\\";
|
2022-04-24 02:53:12 +02:00
|
|
|
const RESET_APPLICATION_MODE: &str = "\x1b[?1l";
|
|
|
|
|
2022-03-16 19:17:06 +01:00
|
|
|
pub fn evaluate_repl(
|
2022-02-19 21:54:43 +01:00
|
|
|
engine_state: &mut EngineState,
|
2022-03-16 19:17:06 +01:00
|
|
|
stack: &mut Stack,
|
2022-06-14 22:53:33 +02:00
|
|
|
nushell_path: &str,
|
2022-08-18 11:25:52 +02:00
|
|
|
prerun_command: Option<Spanned<String>>,
|
2023-05-10 14:05:01 +02:00
|
|
|
load_std_lib: Option<Spanned<String>>,
|
2023-01-24 21:28:59 +01:00
|
|
|
entire_start_time: Instant,
|
2022-02-19 21:54:43 +01:00
|
|
|
) -> Result<()> {
|
2023-08-29 23:46:50 +02:00
|
|
|
use nu_cmd_base::hook;
|
2023-07-07 03:16:17 +02:00
|
|
|
use reedline::Signal;
|
2023-02-02 00:03:05 +01:00
|
|
|
let use_color = engine_state.get_config().use_ansi_coloring;
|
2022-01-18 09:48:28 +01:00
|
|
|
|
2022-09-05 13:33:54 +02:00
|
|
|
// Guard against invocation without a connected terminal.
|
|
|
|
// reedline / crossterm event polling will fail without a connected tty
|
2023-07-12 18:15:54 +02:00
|
|
|
if !std::io::stdin().is_terminal() {
|
2022-09-05 13:33:54 +02:00
|
|
|
return Err(std::io::Error::new(
|
|
|
|
std::io::ErrorKind::NotFound,
|
2022-10-23 08:02:52 +02:00
|
|
|
"Nushell launched as a REPL, but STDIN is not a TTY; either launch in a valid terminal or provide arguments to invoke a script!",
|
2022-09-05 13:33:54 +02:00
|
|
|
))
|
|
|
|
.into_diagnostic();
|
|
|
|
}
|
|
|
|
|
2022-01-18 09:48:28 +01:00
|
|
|
let mut entry_num = 0;
|
|
|
|
|
|
|
|
let mut nu_prompt = NushellPrompt::new();
|
|
|
|
|
2023-01-24 21:28:59 +01:00
|
|
|
let start_time = std::time::Instant::now();
|
2022-01-18 09:48:28 +01:00
|
|
|
// Translate environment variables from Strings to Values
|
2022-03-16 19:17:06 +01:00
|
|
|
if let Some(e) = convert_env_values(engine_state, stack) {
|
2022-01-18 09:48:28 +01:00
|
|
|
let working_set = StateWorkingSet::new(engine_state);
|
|
|
|
report_error(&working_set, &e);
|
|
|
|
}
|
2023-01-24 21:28:59 +01:00
|
|
|
perf(
|
|
|
|
"translate env vars",
|
|
|
|
start_time,
|
|
|
|
file!(),
|
|
|
|
line!(),
|
|
|
|
column!(),
|
2023-02-02 00:03:05 +01:00
|
|
|
use_color,
|
2023-01-24 21:28:59 +01:00
|
|
|
);
|
2022-01-18 09:48:28 +01:00
|
|
|
|
2022-02-26 14:57:45 +01:00
|
|
|
// seed env vars
|
2022-01-21 20:50:44 +01:00
|
|
|
stack.add_env_var(
|
|
|
|
"CMD_DURATION_MS".into(),
|
Reduced LOC by replacing several instances of `Value::Int {}`, `Value::Float{}`, `Value::Bool {}`, and `Value::String {}` with `Value::int()`, `Value::float()`, `Value::boolean()` and `Value::string()` (#7412)
# Description
While perusing Value.rs, I noticed the `Value::int()`, `Value::float()`,
`Value::boolean()` and `Value::string()` constructors, which seem
designed to make it easier to construct various Values, but which aren't
used often at all in the codebase. So, using a few find-replaces
regexes, I increased their usage. This reduces overall LOC because
structures like this:
```
Value::Int {
val: a,
span: head
}
```
are changed into
```
Value::int(a, head)
```
and are respected as such by the project's formatter.
There are little readability concerns because the second argument to all
of these is `span`, and it's almost always extremely obvious which is
the span at every callsite.
# User-Facing Changes
None.
# 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
# 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.
2022-12-09 17:37:51 +01:00
|
|
|
Value::string("0823", Span::unknown()),
|
2022-01-21 20:50:44 +01:00
|
|
|
);
|
|
|
|
|
Reduced LOC by replacing several instances of `Value::Int {}`, `Value::Float{}`, `Value::Bool {}`, and `Value::String {}` with `Value::int()`, `Value::float()`, `Value::boolean()` and `Value::string()` (#7412)
# Description
While perusing Value.rs, I noticed the `Value::int()`, `Value::float()`,
`Value::boolean()` and `Value::string()` constructors, which seem
designed to make it easier to construct various Values, but which aren't
used often at all in the codebase. So, using a few find-replaces
regexes, I increased their usage. This reduces overall LOC because
structures like this:
```
Value::Int {
val: a,
span: head
}
```
are changed into
```
Value::int(a, head)
```
and are respected as such by the project's formatter.
There are little readability concerns because the second argument to all
of these is `span`, and it's almost always extremely obvious which is
the span at every callsite.
# User-Facing Changes
None.
# 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
# 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.
2022-12-09 17:37:51 +01:00
|
|
|
stack.add_env_var("LAST_EXIT_CODE".into(), Value::int(0, Span::unknown()));
|
2022-02-26 14:57:45 +01:00
|
|
|
|
2023-01-24 21:28:59 +01:00
|
|
|
let mut start_time = std::time::Instant::now();
|
2022-04-01 00:16:28 +02:00
|
|
|
let mut line_editor = Reedline::create();
|
2022-09-19 16:28:36 +02:00
|
|
|
|
|
|
|
// Now that reedline is created, get the history session id and store it in engine_state
|
2023-07-07 03:16:17 +02:00
|
|
|
store_history_id_in_engine(engine_state, &line_editor);
|
2023-02-02 00:03:05 +01:00
|
|
|
perf(
|
|
|
|
"setup reedline",
|
|
|
|
start_time,
|
|
|
|
file!(),
|
|
|
|
line!(),
|
|
|
|
column!(),
|
|
|
|
use_color,
|
|
|
|
);
|
2022-09-19 16:28:36 +02:00
|
|
|
|
|
|
|
let config = engine_state.get_config();
|
2023-05-03 23:08:47 +02:00
|
|
|
if config.bracketed_paste {
|
|
|
|
// try to enable bracketed paste
|
|
|
|
// It doesn't work on windows system: https://github.com/crossterm-rs/crossterm/issues/737
|
|
|
|
#[cfg(not(target_os = "windows"))]
|
|
|
|
let _ = line_editor.enable_bracketed_paste();
|
|
|
|
}
|
2022-09-19 16:28:36 +02:00
|
|
|
|
2023-05-01 15:11:38 +02:00
|
|
|
// Setup history_isolation aka "history per session"
|
|
|
|
let history_isolation = config.history_isolation;
|
|
|
|
let history_session_id = if history_isolation {
|
|
|
|
Reedline::create_history_session_id()
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
|
2023-01-24 21:28:59 +01:00
|
|
|
start_time = std::time::Instant::now();
|
2022-06-14 22:53:33 +02:00
|
|
|
let history_path = crate::config_files::get_history_path(
|
|
|
|
nushell_path,
|
|
|
|
engine_state.config.history_file_format,
|
|
|
|
);
|
2022-03-31 23:25:48 +02:00
|
|
|
if let Some(history_path) = history_path.as_deref() {
|
2023-07-07 03:16:17 +02:00
|
|
|
line_editor =
|
|
|
|
update_line_editor_history(engine_state, history_path, line_editor, history_session_id)?
|
2022-03-31 23:25:48 +02:00
|
|
|
};
|
2023-02-02 00:03:05 +01:00
|
|
|
perf(
|
|
|
|
"setup history",
|
|
|
|
start_time,
|
|
|
|
file!(),
|
|
|
|
line!(),
|
|
|
|
column!(),
|
|
|
|
use_color,
|
|
|
|
);
|
2022-03-31 23:25:48 +02:00
|
|
|
|
2023-01-24 21:28:59 +01:00
|
|
|
start_time = std::time::Instant::now();
|
2022-06-14 22:53:33 +02:00
|
|
|
let sys = sysinfo::System::new();
|
2023-01-24 21:28:59 +01:00
|
|
|
perf(
|
2023-05-10 14:05:01 +02:00
|
|
|
"get sysinfo",
|
2023-01-24 21:28:59 +01:00
|
|
|
start_time,
|
|
|
|
file!(),
|
|
|
|
line!(),
|
|
|
|
column!(),
|
2023-02-02 00:03:05 +01:00
|
|
|
use_color,
|
2023-01-24 21:28:59 +01:00
|
|
|
);
|
2022-07-29 19:50:12 +02:00
|
|
|
|
2022-08-18 11:25:52 +02:00
|
|
|
if let Some(s) = prerun_command {
|
|
|
|
eval_source(
|
|
|
|
engine_state,
|
|
|
|
stack,
|
|
|
|
s.item.as_bytes(),
|
2023-01-30 02:37:54 +01:00
|
|
|
&format!("entry #{entry_num}"),
|
2022-12-07 19:31:57 +01:00
|
|
|
PipelineData::empty(),
|
2023-02-02 00:02:27 +01:00
|
|
|
false,
|
2022-08-18 11:25:52 +02:00
|
|
|
);
|
2023-06-04 21:04:28 +02:00
|
|
|
engine_state.merge_env(stack, get_guaranteed_cwd(engine_state, stack))?;
|
2022-08-18 11:25:52 +02:00
|
|
|
}
|
|
|
|
|
2023-05-10 14:05:01 +02:00
|
|
|
engine_state.set_startup_time(entire_start_time.elapsed().as_nanos() as i64);
|
|
|
|
|
2023-09-01 08:18:55 +02:00
|
|
|
// Regenerate the $nu constant to contain the startup time and any other potential updates
|
|
|
|
let nu_const = create_nu_constant(engine_state, Span::unknown())?;
|
|
|
|
engine_state.set_variable_const_val(NU_VARIABLE_ID, nu_const);
|
|
|
|
|
2023-05-10 14:05:01 +02:00
|
|
|
if load_std_lib.is_none() && engine_state.get_config().show_banner {
|
|
|
|
eval_source(
|
|
|
|
engine_state,
|
|
|
|
stack,
|
|
|
|
r#"use std banner; banner"#.as_bytes(),
|
|
|
|
"show_banner",
|
|
|
|
PipelineData::empty(),
|
|
|
|
false,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-01-18 09:48:28 +01:00
|
|
|
loop {
|
2023-01-24 21:28:59 +01:00
|
|
|
let loop_start_time = std::time::Instant::now();
|
2022-02-10 22:22:39 +01:00
|
|
|
|
2023-06-04 21:04:28 +02:00
|
|
|
let cwd = get_guaranteed_cwd(engine_state, stack);
|
|
|
|
|
2023-01-24 21:28:59 +01:00
|
|
|
start_time = std::time::Instant::now();
|
2022-07-14 16:09:27 +02:00
|
|
|
// Before doing anything, merge the environment from the previous REPL iteration into the
|
|
|
|
// permanent state.
|
2023-06-04 21:04:28 +02:00
|
|
|
if let Err(err) = engine_state.merge_env(stack, cwd) {
|
2022-07-14 16:09:27 +02:00
|
|
|
report_error_new(engine_state, &err);
|
|
|
|
}
|
2023-02-02 00:03:05 +01:00
|
|
|
perf(
|
|
|
|
"merge env",
|
|
|
|
start_time,
|
|
|
|
file!(),
|
|
|
|
line!(),
|
|
|
|
column!(),
|
|
|
|
use_color,
|
|
|
|
);
|
2022-07-14 16:09:27 +02:00
|
|
|
|
2023-01-24 21:28:59 +01:00
|
|
|
start_time = std::time::Instant::now();
|
2022-04-19 00:28:01 +02:00
|
|
|
//Reset the ctrl-c handler
|
|
|
|
if let Some(ctrlc) = &mut engine_state.ctrlc {
|
|
|
|
ctrlc.store(false, Ordering::SeqCst);
|
|
|
|
}
|
2023-02-02 00:03:05 +01:00
|
|
|
perf(
|
|
|
|
"reset ctrlc",
|
|
|
|
start_time,
|
|
|
|
file!(),
|
|
|
|
line!(),
|
|
|
|
column!(),
|
|
|
|
use_color,
|
|
|
|
);
|
2023-01-24 21:28:59 +01:00
|
|
|
|
|
|
|
start_time = std::time::Instant::now();
|
2022-06-09 14:08:15 +02:00
|
|
|
// Reset the SIGQUIT handler
|
|
|
|
if let Some(sig_quit) = engine_state.get_sig_quit() {
|
|
|
|
sig_quit.store(false, Ordering::SeqCst);
|
|
|
|
}
|
2023-02-02 00:03:05 +01:00
|
|
|
perf(
|
|
|
|
"reset sig_quit",
|
|
|
|
start_time,
|
|
|
|
file!(),
|
|
|
|
line!(),
|
|
|
|
column!(),
|
|
|
|
use_color,
|
|
|
|
);
|
2022-01-18 09:48:28 +01:00
|
|
|
|
2023-01-24 21:28:59 +01:00
|
|
|
start_time = std::time::Instant::now();
|
2022-07-10 12:45:46 +02:00
|
|
|
let config = engine_state.get_config();
|
2022-04-11 20:19:42 +02:00
|
|
|
|
2022-04-06 14:25:02 +02:00
|
|
|
let engine_reference = std::sync::Arc::new(engine_state.clone());
|
2023-01-13 21:37:39 +01:00
|
|
|
|
|
|
|
// Find the configured cursor shapes for each mode
|
|
|
|
let cursor_config = CursorConfig {
|
Make cursor_shape optional (#10289)
# Description
There are several cursor shape related issues #7151 #9243 #7271 #8452
#10169, you can't disable the cursor shape feature even if you comment
out the entire `cursor_shape` block in the config.nu, and even worse,
when nushell exits with an error, the cursor shape can't be restored,
that is annoying.
This PR provides an opportunity to disable setting the cursor shape.
# User-Facing Changes
If you use the default config.nu, nothing changes, but if you comment
out `cursor_shape` block or set them to `inherit`, related cursor shape
will not be set.
# 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` to
check that you're using the standard code style
- `cargo test --workspace` to check that all tests pass (on Windows make
sure to [enable developer
mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging))
- `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-09-09 20:42:36 +02:00
|
|
|
vi_insert: config
|
|
|
|
.cursor_shape_vi_insert
|
|
|
|
.map(map_nucursorshape_to_cursorshape),
|
|
|
|
vi_normal: config
|
|
|
|
.cursor_shape_vi_normal
|
|
|
|
.map(map_nucursorshape_to_cursorshape),
|
|
|
|
emacs: config
|
|
|
|
.cursor_shape_emacs
|
|
|
|
.map(map_nucursorshape_to_cursorshape),
|
2023-01-13 21:37:39 +01:00
|
|
|
};
|
2023-01-24 21:28:59 +01:00
|
|
|
perf(
|
|
|
|
"get config/cursor config",
|
|
|
|
start_time,
|
|
|
|
file!(),
|
|
|
|
line!(),
|
|
|
|
column!(),
|
2023-02-02 00:03:05 +01:00
|
|
|
use_color,
|
2023-01-24 21:28:59 +01:00
|
|
|
);
|
|
|
|
|
|
|
|
start_time = std::time::Instant::now();
|
2023-01-13 21:37:39 +01:00
|
|
|
|
2022-03-31 23:25:48 +02:00
|
|
|
line_editor = line_editor
|
2022-01-18 09:48:28 +01:00
|
|
|
.with_highlighter(Box::new(NuHighlighter {
|
2023-01-11 02:22:32 +01:00
|
|
|
engine_state: engine_reference.clone(),
|
2022-01-18 09:48:28 +01:00
|
|
|
config: config.clone(),
|
|
|
|
}))
|
|
|
|
.with_validator(Box::new(NuValidator {
|
2023-01-11 02:22:32 +01:00
|
|
|
engine_state: engine_reference.clone(),
|
2022-01-18 09:48:28 +01:00
|
|
|
}))
|
2022-02-18 19:54:13 +01:00
|
|
|
.with_completer(Box::new(NuCompleter::new(
|
2022-04-06 14:25:02 +02:00
|
|
|
engine_reference.clone(),
|
2022-03-28 19:49:41 +02:00
|
|
|
stack.clone(),
|
2022-02-18 19:54:13 +01:00
|
|
|
)))
|
2022-02-04 16:30:21 +01:00
|
|
|
.with_quick_completions(config.quick_completions)
|
2022-03-03 10:13:44 +01:00
|
|
|
.with_partial_completions(config.partial_completions)
|
2023-01-13 21:37:39 +01:00
|
|
|
.with_ansi_colors(config.use_ansi_coloring)
|
Transient prompt (#10391)
## Description
This PR uses environment variables to enable and set a transient prompt,
which lets you draw a different prompt once you've entered a command and
you've moved on to the next line. This is useful if you have a fancy
two-line prompt with a bunch of info about time and git status that you
don't really need in your scrollback buffer.
Here's a screenshot. You can see how my usual prompt has two lines and
would take up a lot more space if every past command also used the full
prompt, but reducing past prompts to `🚀` or `>` makes it take up less
space.
![image](https://github.com/nushell/nushell/assets/45539777/dde8d0f5-f95f-4529-9a14-b7919bd51126)
I added the following lines to my `env.nu` to get that rocket as the
prompt initially:
```nu
$env.TRANSIENT_PROMPT_COMMAND = {|| "" }
$env.TRANSIENT_PROMPT_INDICATOR = {|| open --raw "~/.prompt-indicator" }
$env.TRANSIENT_PROMPT_INDICATOR_VI_INSERT = $env.TRANSIENT_PROMPT_INDICATOR
```
## User-Facing Changes
If you want to change a segment of the prompt, set the corresponding
`TRANSIENT_PROMPT_*` variable.
<!-- 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.
-->
## Problems/Things to Consider:
- The transient prompt clones the `Stack` at the very beginning of the
session and keeps that around. I'm not sure if that could cause
problems, but if so, it could probably take an `Arc<State>` instead.
- This isn't truly a problem, but now there's even more environment
variables, which is kinda annoying.
- There might be some performance issues with creating a new
`NushellPrompt` object and cloning the `Stack` for every segment of the
transient prompt. What's more, the transient prompt is added to the
`Reedline` object whether or not the user has enabled transient prompt,
so if there are indeed performance issues, simply disabling the
transient prompt won't help.
- Perhaps instead of a separate `TRANSIENT_PROMPT_INDICATOR_VI_INSERT`
and `TRANSIENT_PROMPT_INDICATOR_VI_NORMAL`, `TRANSIENT_PROMPT_INDICATOR`
could be used for both (if it exists). Insert and normal mode don't
really matter for previously entered commands.
2023-09-22 21:35:09 +02:00
|
|
|
.with_cursor_config(cursor_config)
|
|
|
|
.with_transient_prompt(prompt_update::transient_prompt(
|
|
|
|
engine_reference.clone(),
|
|
|
|
stack,
|
|
|
|
));
|
2023-02-02 00:03:05 +01:00
|
|
|
perf(
|
|
|
|
"reedline builder",
|
|
|
|
start_time,
|
|
|
|
file!(),
|
|
|
|
line!(),
|
|
|
|
column!(),
|
|
|
|
use_color,
|
|
|
|
);
|
2022-01-27 08:53:23 +01:00
|
|
|
|
color_config now accepts closures as color values (#7141)
# Description
Closes #6909. You can now add closures to your `color_config` themes.
Whenever a value would be printed with `table`, the closure is run with
the value piped-in. The closure must return either a {fg,bg,attr} record
or a color name (`'light_red'` etc.). This returned style is used to
colour the value.
This is entirely backwards-compatible with existing config.nu files.
Example code excerpt:
```
let my_theme = {
header: green_bold
bool: { if $in { 'light_cyan' } else { 'light_red' } }
int: purple_bold
filesize: { |e| if $e == 0b { 'gray' } else if $e < 1mb { 'purple_bold' } else { 'cyan_bold' } }
duration: purple_bold
date: { (date now) - $in | if $in > 1wk { 'cyan_bold' } else if $in > 1day { 'green_bold' } else { 'yellow_bold' } }
range: yellow_bold
string: { if $in =~ '^#\w{6}$' { $in } else { 'white' } }
nothing: white
```
Example output with this in effect:
![2022-11-16 12 47 23 AM - style_computer
rs_-_nushell_-_VSCodium](https://user-images.githubusercontent.com/83939/201952558-482de05d-69c7-4bf2-91fc-d0964bf71264.png)
![2022-11-16 12 39 41 AM - style_computer
rs_-_nushell_-_VSCodium](https://user-images.githubusercontent.com/83939/201952580-2384bb86-b680-40fe-8192-71bae396c738.png)
![2022-11-15 09 21 54 PM - run_external
rs_-_nushell_-_VSCodium](https://user-images.githubusercontent.com/83939/201952601-343fc15d-e4a8-4a92-ad89-9a7d17d42748.png)
Slightly important notes:
* Some color_config names, namely "separator", "empty" and "hints", pipe
in `null` instead of a value.
* Currently, doing anything non-trivial inside a closure has an
understandably big perf hit. I currently do not actually recommend
something like `string: { if $in =~ '^#\w{6}$' { $in } else { 'white' }
}` for serious work, mainly because of the abundance of string-type data
in the world. Nevertheless, lesser-used types like "date" and "duration"
work well with this.
* I had to do some reorganisation in order to make it possible to call
`eval_block()` that late in table rendering. I invented a new struct
called "StyleComputer" which holds the engine_state and stack of the
initial `table` command (implicit or explicit).
* StyleComputer has a `compute()` method which takes a color_config name
and a nu value, and always returns the correct Style, so you don't have
to worry about A) the color_config value was set at all, B) whether it
was set to a closure or not, or C) which default style to use in those
cases.
* Currently, errors encountered during execution of the closures are
thrown in the garbage. Any other ideas are welcome. (Nonetheless, errors
result in a huge perf hit when they are encountered. I think what should
be done is to assume something terrible happened to the user's config
and invalidate the StyleComputer for that `table` run, thus causing
subsequent output to just be Style::default().)
* More thorough tests are forthcoming - ran into some difficulty using
`nu!` to take an alternative config, and for some reason `let-env config
=` statements don't seem to work inside `nu!` pipelines(???)
* The default config.nu has not been updated to make use of this yet. Do
tell if you think I should incorporate that into this.
# 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 --features=extra -- -D warnings -D
clippy::unwrap_used -A clippy::needless_collect` to check that you're
using the standard code style
- `cargo test --workspace --features=extra` to check that all tests pass
# 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.
2022-12-17 14:07:56 +01:00
|
|
|
let style_computer = StyleComputer::from_config(engine_state, stack);
|
|
|
|
|
2023-01-24 21:28:59 +01:00
|
|
|
start_time = std::time::Instant::now();
|
2022-04-11 20:19:42 +02:00
|
|
|
line_editor = if config.use_ansi_coloring {
|
color_config now accepts closures as color values (#7141)
# Description
Closes #6909. You can now add closures to your `color_config` themes.
Whenever a value would be printed with `table`, the closure is run with
the value piped-in. The closure must return either a {fg,bg,attr} record
or a color name (`'light_red'` etc.). This returned style is used to
colour the value.
This is entirely backwards-compatible with existing config.nu files.
Example code excerpt:
```
let my_theme = {
header: green_bold
bool: { if $in { 'light_cyan' } else { 'light_red' } }
int: purple_bold
filesize: { |e| if $e == 0b { 'gray' } else if $e < 1mb { 'purple_bold' } else { 'cyan_bold' } }
duration: purple_bold
date: { (date now) - $in | if $in > 1wk { 'cyan_bold' } else if $in > 1day { 'green_bold' } else { 'yellow_bold' } }
range: yellow_bold
string: { if $in =~ '^#\w{6}$' { $in } else { 'white' } }
nothing: white
```
Example output with this in effect:
![2022-11-16 12 47 23 AM - style_computer
rs_-_nushell_-_VSCodium](https://user-images.githubusercontent.com/83939/201952558-482de05d-69c7-4bf2-91fc-d0964bf71264.png)
![2022-11-16 12 39 41 AM - style_computer
rs_-_nushell_-_VSCodium](https://user-images.githubusercontent.com/83939/201952580-2384bb86-b680-40fe-8192-71bae396c738.png)
![2022-11-15 09 21 54 PM - run_external
rs_-_nushell_-_VSCodium](https://user-images.githubusercontent.com/83939/201952601-343fc15d-e4a8-4a92-ad89-9a7d17d42748.png)
Slightly important notes:
* Some color_config names, namely "separator", "empty" and "hints", pipe
in `null` instead of a value.
* Currently, doing anything non-trivial inside a closure has an
understandably big perf hit. I currently do not actually recommend
something like `string: { if $in =~ '^#\w{6}$' { $in } else { 'white' }
}` for serious work, mainly because of the abundance of string-type data
in the world. Nevertheless, lesser-used types like "date" and "duration"
work well with this.
* I had to do some reorganisation in order to make it possible to call
`eval_block()` that late in table rendering. I invented a new struct
called "StyleComputer" which holds the engine_state and stack of the
initial `table` command (implicit or explicit).
* StyleComputer has a `compute()` method which takes a color_config name
and a nu value, and always returns the correct Style, so you don't have
to worry about A) the color_config value was set at all, B) whether it
was set to a closure or not, or C) which default style to use in those
cases.
* Currently, errors encountered during execution of the closures are
thrown in the garbage. Any other ideas are welcome. (Nonetheless, errors
result in a huge perf hit when they are encountered. I think what should
be done is to assume something terrible happened to the user's config
and invalidate the StyleComputer for that `table` run, thus causing
subsequent output to just be Style::default().)
* More thorough tests are forthcoming - ran into some difficulty using
`nu!` to take an alternative config, and for some reason `let-env config
=` statements don't seem to work inside `nu!` pipelines(???)
* The default config.nu has not been updated to make use of this yet. Do
tell if you think I should incorporate that into this.
# 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 --features=extra -- -D warnings -D
clippy::unwrap_used -A clippy::needless_collect` to check that you're
using the standard code style
- `cargo test --workspace --features=extra` to check that all tests pass
# 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.
2022-12-17 14:07:56 +01:00
|
|
|
line_editor.with_hinter(Box::new({
|
|
|
|
// As of Nov 2022, "hints" color_config closures only get `null` passed in.
|
|
|
|
let style = style_computer.compute("hints", &Value::nothing(Span::unknown()));
|
|
|
|
DefaultHinter::default().with_style(style)
|
|
|
|
}))
|
2022-04-11 20:19:42 +02:00
|
|
|
} else {
|
|
|
|
line_editor.disable_hints()
|
|
|
|
};
|
2023-01-24 21:28:59 +01:00
|
|
|
perf(
|
|
|
|
"reedline coloring/style_computer",
|
|
|
|
start_time,
|
|
|
|
file!(),
|
|
|
|
line!(),
|
|
|
|
column!(),
|
2023-02-02 00:03:05 +01:00
|
|
|
use_color,
|
2023-01-24 21:28:59 +01:00
|
|
|
);
|
2022-02-10 22:22:39 +01:00
|
|
|
|
2023-01-24 21:28:59 +01:00
|
|
|
start_time = std::time::Instant::now();
|
2023-01-21 14:47:00 +01:00
|
|
|
line_editor = add_menus(line_editor, engine_reference, stack, config).unwrap_or_else(|e| {
|
|
|
|
let working_set = StateWorkingSet::new(engine_state);
|
|
|
|
report_error(&working_set, &e);
|
|
|
|
Reedline::create()
|
|
|
|
});
|
2023-02-02 00:03:05 +01:00
|
|
|
perf(
|
|
|
|
"reedline menus",
|
|
|
|
start_time,
|
|
|
|
file!(),
|
|
|
|
line!(),
|
|
|
|
column!(),
|
|
|
|
use_color,
|
|
|
|
);
|
2022-03-27 15:01:04 +02:00
|
|
|
|
2023-01-24 21:28:59 +01:00
|
|
|
start_time = std::time::Instant::now();
|
2022-05-22 19:32:52 +02:00
|
|
|
let buffer_editor = if !config.buffer_editor.is_empty() {
|
|
|
|
Some(config.buffer_editor.clone())
|
|
|
|
} else {
|
|
|
|
stack
|
|
|
|
.get_env_var(engine_state, "EDITOR")
|
|
|
|
.map(|v| v.as_string().unwrap_or_default())
|
|
|
|
.filter(|v| !v.is_empty())
|
|
|
|
.or_else(|| {
|
|
|
|
stack
|
|
|
|
.get_env_var(engine_state, "VISUAL")
|
|
|
|
.map(|v| v.as_string().unwrap_or_default())
|
|
|
|
.filter(|v| !v.is_empty())
|
|
|
|
})
|
|
|
|
};
|
|
|
|
|
|
|
|
line_editor = if let Some(buffer_editor) = buffer_editor {
|
|
|
|
line_editor.with_buffer_editor(buffer_editor, "nu".into())
|
|
|
|
} else {
|
|
|
|
line_editor
|
|
|
|
};
|
2023-01-24 21:28:59 +01:00
|
|
|
perf(
|
|
|
|
"reedline buffer_editor",
|
|
|
|
start_time,
|
|
|
|
file!(),
|
|
|
|
line!(),
|
|
|
|
column!(),
|
2023-02-02 00:03:05 +01:00
|
|
|
use_color,
|
2023-01-24 21:28:59 +01:00
|
|
|
);
|
2022-04-30 16:40:41 +02:00
|
|
|
|
2023-01-24 21:28:59 +01:00
|
|
|
start_time = std::time::Instant::now();
|
2022-03-31 23:25:48 +02:00
|
|
|
if config.sync_history_on_enter {
|
2022-07-23 18:35:43 +02:00
|
|
|
if let Err(e) = line_editor.sync_history() {
|
|
|
|
warn!("Failed to sync history: {}", e);
|
|
|
|
}
|
2022-03-31 23:25:48 +02:00
|
|
|
}
|
2023-02-02 00:03:05 +01:00
|
|
|
perf(
|
|
|
|
"sync_history",
|
|
|
|
start_time,
|
|
|
|
file!(),
|
|
|
|
line!(),
|
|
|
|
column!(),
|
|
|
|
use_color,
|
|
|
|
);
|
2022-01-18 09:48:28 +01:00
|
|
|
|
2023-01-24 21:28:59 +01:00
|
|
|
start_time = std::time::Instant::now();
|
2022-01-18 20:32:45 +01:00
|
|
|
// Changing the line editor based on the found keybindings
|
2022-05-05 17:10:03 +02:00
|
|
|
line_editor = match create_keybindings(config) {
|
2022-01-19 14:28:08 +01:00
|
|
|
Ok(keybindings) => match keybindings {
|
|
|
|
KeybindingsMode::Emacs(keybindings) => {
|
|
|
|
let edit_mode = Box::new(Emacs::new(keybindings));
|
|
|
|
line_editor.with_edit_mode(edit_mode)
|
|
|
|
}
|
|
|
|
KeybindingsMode::Vi {
|
|
|
|
insert_keybindings,
|
|
|
|
normal_keybindings,
|
|
|
|
} => {
|
|
|
|
let edit_mode = Box::new(Vi::new(insert_keybindings, normal_keybindings));
|
|
|
|
line_editor.with_edit_mode(edit_mode)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
Err(e) => {
|
|
|
|
let working_set = StateWorkingSet::new(engine_state);
|
|
|
|
report_error(&working_set, &e);
|
|
|
|
line_editor
|
2022-01-18 20:32:45 +01:00
|
|
|
}
|
2022-01-18 09:48:28 +01:00
|
|
|
};
|
2023-02-02 00:03:05 +01:00
|
|
|
perf(
|
|
|
|
"keybindings",
|
|
|
|
start_time,
|
|
|
|
file!(),
|
|
|
|
line!(),
|
|
|
|
column!(),
|
|
|
|
use_color,
|
|
|
|
);
|
2022-01-18 09:48:28 +01:00
|
|
|
|
2023-01-24 21:28:59 +01:00
|
|
|
start_time = std::time::Instant::now();
|
2022-05-09 03:56:48 +02:00
|
|
|
// Right before we start our prompt and take input from the user,
|
|
|
|
// fire the "pre_prompt" hook
|
2022-07-10 12:45:46 +02:00
|
|
|
if let Some(hook) = config.hooks.pre_prompt.clone() {
|
2023-08-27 13:55:20 +02:00
|
|
|
if let Err(err) = eval_hook(engine_state, stack, None, vec![], &hook, "pre_prompt") {
|
2022-07-10 12:45:46 +02:00
|
|
|
report_error_new(engine_state, &err);
|
2022-05-09 03:56:48 +02:00
|
|
|
}
|
|
|
|
}
|
2023-02-02 00:03:05 +01:00
|
|
|
perf(
|
|
|
|
"pre-prompt hook",
|
|
|
|
start_time,
|
|
|
|
file!(),
|
|
|
|
line!(),
|
|
|
|
column!(),
|
|
|
|
use_color,
|
|
|
|
);
|
2022-05-09 03:56:48 +02:00
|
|
|
|
2023-01-24 21:28:59 +01:00
|
|
|
start_time = std::time::Instant::now();
|
2022-05-20 23:49:42 +02:00
|
|
|
// Next, check all the environment variables they ask for
|
|
|
|
// fire the "env_change" hook
|
2022-07-10 12:45:46 +02:00
|
|
|
let config = engine_state.get_config();
|
|
|
|
if let Err(error) =
|
2023-03-20 05:05:22 +01:00
|
|
|
hook::eval_env_change_hook(config.hooks.env_change.clone(), engine_state, stack)
|
2022-07-10 12:45:46 +02:00
|
|
|
{
|
|
|
|
report_error_new(engine_state, &error)
|
2022-05-20 23:49:42 +02:00
|
|
|
}
|
2023-02-02 00:03:05 +01:00
|
|
|
perf(
|
|
|
|
"env-change hook",
|
|
|
|
start_time,
|
|
|
|
file!(),
|
|
|
|
line!(),
|
|
|
|
column!(),
|
|
|
|
use_color,
|
|
|
|
);
|
2022-05-20 23:49:42 +02:00
|
|
|
|
2023-01-24 21:28:59 +01:00
|
|
|
start_time = std::time::Instant::now();
|
FEATURE: add the startup time to `$nu` (#8353)
# Description
in https://github.com/nushell/nushell/issues/8311 and the discord
server, the idea of moving the default banner from the `rust` source to
the `nushell` standar library has emerged :yum:
however, in order to do this, one need to have access to all the
variables used in the default banner => all of them are accessible
because known constants, except for the startup time of the shell, which
is not anywhere in the shell...
#### this PR adds exactly this, i.e. the new `startup_time` to the `$nu`
variable, which is computed to have the exact same value as the value
shown in the banner.
## the changes
in order to achieve this, i had to
- add `startup_time` as an `i64` to the `EngineState` => this is, to the
best of my knowledge, the easiest way to pass such an information around
down to where the banner startup time is computed and where the `$nu`
variable is evaluated
- add `startup-time` to the `$nu` variable and use the `EngineState`
getter for `startup_time` to show it as a `Value::Duration`
- pass `engine_state` as a `&mut`able argument from `main.rs` down to
`repl.rs` to allow the setter to change the value of `startup_time` =>
without this, the value would not change and would show `-1ns` as the
default value...
- the value of the startup time is computed in `evaluate_repl` in
`repl.rs`, only once at the beginning, and the same value is used in the
default banner :ok_hand:
# User-Facing Changes
one can now access to the same time as shown in the default banner with
```bash
$nu.startup-time
```
# Tests + Formatting
- :green_circle: `cargo fmt --all`
- :green_circle: `cargo clippy --workspace -- -D warnings -D
clippy::unwrap_used -A clippy::needless_collect`
- :green_circle: `cargo test --workspace`
# After Submitting
```
$nothing
```
2023-03-09 21:18:58 +01:00
|
|
|
let config = &engine_state.get_config().clone();
|
2022-10-21 17:20:21 +02:00
|
|
|
let prompt = prompt_update::update_prompt(config, engine_state, stack, &mut nu_prompt);
|
2023-02-02 00:03:05 +01:00
|
|
|
perf(
|
|
|
|
"update_prompt",
|
|
|
|
start_time,
|
|
|
|
file!(),
|
|
|
|
line!(),
|
|
|
|
column!(),
|
|
|
|
use_color,
|
|
|
|
);
|
2022-01-18 09:48:28 +01:00
|
|
|
|
2022-02-10 22:22:39 +01:00
|
|
|
entry_num += 1;
|
|
|
|
|
2023-01-24 21:28:59 +01:00
|
|
|
start_time = std::time::Instant::now();
|
2022-01-18 09:48:28 +01:00
|
|
|
let input = line_editor.read_line(prompt);
|
2022-07-20 22:03:29 +02:00
|
|
|
let shell_integration = config.shell_integration;
|
2022-04-17 05:03:02 +02:00
|
|
|
|
2022-01-18 09:48:28 +01:00
|
|
|
match input {
|
|
|
|
Ok(Signal::Success(s)) => {
|
2022-09-13 14:36:53 +02:00
|
|
|
let hostname = sys.host_name();
|
2022-06-14 22:53:33 +02:00
|
|
|
let history_supports_meta =
|
|
|
|
matches!(config.history_file_format, HistoryFileFormat::Sqlite);
|
2022-09-05 13:31:26 +02:00
|
|
|
if history_supports_meta && !s.is_empty() && line_editor.has_last_command_context()
|
|
|
|
{
|
2022-06-14 22:53:33 +02:00
|
|
|
line_editor
|
|
|
|
.update_last_command_context(&|mut c| {
|
|
|
|
c.start_timestamp = Some(chrono::Utc::now());
|
2022-09-13 14:36:53 +02:00
|
|
|
c.hostname = hostname.clone();
|
2022-06-14 22:53:33 +02:00
|
|
|
|
|
|
|
c.cwd = Some(StateWorkingSet::new(engine_state).get_cwd());
|
|
|
|
c
|
|
|
|
})
|
|
|
|
.into_diagnostic()?; // todo: don't stop repl if error here?
|
|
|
|
}
|
|
|
|
|
2023-03-22 12:43:25 +01:00
|
|
|
// Right before we start running the code the user gave us, fire the `pre_execution`
|
|
|
|
// hook
|
2022-07-10 12:45:46 +02:00
|
|
|
if let Some(hook) = config.hooks.pre_execution.clone() {
|
2023-03-16 23:45:35 +01:00
|
|
|
// Set the REPL buffer to the current command for the "pre_execution" hook
|
2023-06-11 00:38:11 +02:00
|
|
|
let mut repl = engine_state.repl_state.lock().expect("repl state mutex");
|
|
|
|
repl.buffer = s.to_string();
|
|
|
|
drop(repl);
|
2023-03-16 23:45:35 +01:00
|
|
|
|
2023-08-27 13:55:20 +02:00
|
|
|
if let Err(err) =
|
|
|
|
eval_hook(engine_state, stack, None, vec![], &hook, "pre_execution")
|
|
|
|
{
|
2022-07-10 12:45:46 +02:00
|
|
|
report_error_new(engine_state, &err);
|
2022-05-08 21:28:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-11 00:38:11 +02:00
|
|
|
let mut repl = engine_state.repl_state.lock().expect("repl state mutex");
|
|
|
|
repl.cursor_pos = line_editor.current_insertion_point();
|
|
|
|
repl.buffer = line_editor.current_buffer_contents().to_string();
|
|
|
|
drop(repl);
|
2023-03-22 12:43:25 +01:00
|
|
|
|
2022-06-03 00:57:19 +02:00
|
|
|
if shell_integration {
|
|
|
|
run_ansi_sequence(PRE_EXECUTE_MARKER)?;
|
2022-05-10 23:33:18 +02:00
|
|
|
}
|
|
|
|
|
2022-01-21 15:53:49 +01:00
|
|
|
let start_time = Instant::now();
|
2022-01-18 09:48:28 +01:00
|
|
|
let tokens = lex(s.as_bytes(), 0, &[], &[], false);
|
|
|
|
// Check if this is a single call to a directory, if so auto-cd
|
2022-03-16 19:17:06 +01:00
|
|
|
let cwd = nu_engine::env::current_dir_str(engine_state, stack)?;
|
2022-01-18 09:48:28 +01:00
|
|
|
|
2022-10-14 23:37:31 +02:00
|
|
|
let mut orig = s.clone();
|
|
|
|
if orig.starts_with('`') {
|
|
|
|
orig = trim_quotes_str(&orig).to_string()
|
|
|
|
}
|
|
|
|
|
|
|
|
let path = nu_path::expand_path_with(&orig, &cwd);
|
2022-01-18 09:48:28 +01:00
|
|
|
|
2022-07-16 04:01:38 +02:00
|
|
|
if looks_like_path(&orig) && path.is_dir() && tokens.0.len() == 1 {
|
2022-01-18 09:48:28 +01:00
|
|
|
// We have an auto-cd
|
|
|
|
let (path, span) = {
|
|
|
|
if !path.exists() {
|
|
|
|
let working_set = StateWorkingSet::new(engine_state);
|
|
|
|
|
|
|
|
report_error(
|
|
|
|
&working_set,
|
2022-04-18 14:34:10 +02:00
|
|
|
&ShellError::DirectoryNotFound(tokens.0[0].span, None),
|
2022-01-18 09:48:28 +01:00
|
|
|
);
|
|
|
|
}
|
|
|
|
let path = nu_path::canonicalize_with(path, &cwd)
|
|
|
|
.expect("internal error: cannot canonicalize known path");
|
|
|
|
(path.to_string_lossy().to_string(), tokens.0[0].span)
|
|
|
|
};
|
|
|
|
|
2023-09-03 16:27:29 +02:00
|
|
|
stack.add_env_var("OLDPWD".into(), Value::string(cwd.clone(), Span::unknown()));
|
2022-07-12 13:05:19 +02:00
|
|
|
|
2022-01-18 09:48:28 +01:00
|
|
|
//FIXME: this only changes the current scope, but instead this environment variable
|
|
|
|
//should probably be a block that loads the information from the state in the overlay
|
2023-09-03 16:27:29 +02:00
|
|
|
stack.add_env_var("PWD".into(), Value::string(path.clone(), Span::unknown()));
|
|
|
|
let cwd = Value::string(cwd, span);
|
2022-01-18 09:48:28 +01:00
|
|
|
|
|
|
|
let shells = stack.get_env_var(engine_state, "NUSHELL_SHELLS");
|
|
|
|
let mut shells = if let Some(v) = shells {
|
|
|
|
v.as_list()
|
|
|
|
.map(|x| x.to_vec())
|
|
|
|
.unwrap_or_else(|_| vec![cwd])
|
|
|
|
} else {
|
|
|
|
vec![cwd]
|
|
|
|
};
|
|
|
|
|
|
|
|
let current_shell = stack.get_env_var(engine_state, "NUSHELL_CURRENT_SHELL");
|
|
|
|
let current_shell = if let Some(v) = current_shell {
|
2023-07-21 15:20:33 +02:00
|
|
|
v.as_int().unwrap_or_default() as usize
|
2022-01-18 09:48:28 +01:00
|
|
|
} else {
|
|
|
|
0
|
|
|
|
};
|
|
|
|
|
2022-08-06 17:11:03 +02:00
|
|
|
let last_shell = stack.get_env_var(engine_state, "NUSHELL_LAST_SHELL");
|
|
|
|
let last_shell = if let Some(v) = last_shell {
|
2023-07-21 15:20:33 +02:00
|
|
|
v.as_int().unwrap_or_default() as usize
|
2022-08-06 17:11:03 +02:00
|
|
|
} else {
|
|
|
|
0
|
|
|
|
};
|
|
|
|
|
2023-09-03 16:27:29 +02:00
|
|
|
shells[current_shell] = Value::string(path, span);
|
2022-01-18 09:48:28 +01:00
|
|
|
|
2023-09-03 16:27:29 +02:00
|
|
|
stack.add_env_var("NUSHELL_SHELLS".into(), Value::list(shells, span));
|
2022-08-06 17:11:03 +02:00
|
|
|
stack.add_env_var(
|
|
|
|
"NUSHELL_LAST_SHELL".into(),
|
2023-09-03 16:27:29 +02:00
|
|
|
Value::int(last_shell as i64, span),
|
2022-08-06 17:11:03 +02:00
|
|
|
);
|
2022-10-08 23:38:35 +02:00
|
|
|
} else if !s.trim().is_empty() {
|
2022-01-18 09:48:28 +01:00
|
|
|
trace!("eval source: {}", s);
|
|
|
|
|
2023-05-30 16:38:45 +02:00
|
|
|
let mut cmds = s.split_whitespace();
|
|
|
|
if let Some("exit") = cmds.next() {
|
|
|
|
let mut working_set = StateWorkingSet::new(engine_state);
|
|
|
|
let _ = parse(&mut working_set, None, s.as_bytes(), false);
|
|
|
|
|
|
|
|
if working_set.parse_errors.is_empty() {
|
|
|
|
match cmds.next() {
|
|
|
|
Some(s) => {
|
|
|
|
if let Ok(n) = s.parse::<i32>() {
|
|
|
|
drop(line_editor);
|
|
|
|
std::process::exit(n);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None => {
|
|
|
|
drop(line_editor);
|
|
|
|
std::process::exit(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-18 09:48:28 +01:00
|
|
|
eval_source(
|
|
|
|
engine_state,
|
2022-03-16 19:17:06 +01:00
|
|
|
stack,
|
2022-02-14 16:53:48 +01:00
|
|
|
s.as_bytes(),
|
2023-01-30 02:37:54 +01:00
|
|
|
&format!("entry #{entry_num}"),
|
2022-12-07 19:31:57 +01:00
|
|
|
PipelineData::empty(),
|
2023-02-02 00:02:27 +01:00
|
|
|
false,
|
2022-01-18 09:48:28 +01:00
|
|
|
);
|
2023-06-10 16:42:13 +02:00
|
|
|
if engine_state.get_config().bracketed_paste {
|
|
|
|
#[cfg(not(target_os = "windows"))]
|
|
|
|
let _ = line_editor.enable_bracketed_paste();
|
|
|
|
}
|
2022-01-18 09:48:28 +01:00
|
|
|
}
|
2022-06-14 22:53:33 +02:00
|
|
|
let cmd_duration = start_time.elapsed();
|
2022-05-05 17:10:03 +02:00
|
|
|
|
|
|
|
stack.add_env_var(
|
|
|
|
"CMD_DURATION_MS".into(),
|
2023-09-03 16:27:29 +02:00
|
|
|
Value::string(format!("{}", cmd_duration.as_millis()), Span::unknown()),
|
2022-05-05 17:10:03 +02:00
|
|
|
);
|
|
|
|
|
2022-09-05 13:31:26 +02:00
|
|
|
if history_supports_meta && !s.is_empty() && line_editor.has_last_command_context()
|
|
|
|
{
|
2022-06-14 22:53:33 +02:00
|
|
|
line_editor
|
|
|
|
.update_last_command_context(&|mut c| {
|
|
|
|
c.duration = Some(cmd_duration);
|
|
|
|
c.exit_status = stack
|
|
|
|
.get_env_var(engine_state, "LAST_EXIT_CODE")
|
|
|
|
.and_then(|e| e.as_i64().ok());
|
|
|
|
c
|
|
|
|
})
|
|
|
|
.into_diagnostic()?; // todo: don't stop repl if error here?
|
|
|
|
}
|
|
|
|
|
2022-06-03 00:57:19 +02:00
|
|
|
if shell_integration {
|
2022-07-20 22:03:29 +02:00
|
|
|
run_ansi_sequence(&get_command_finished_marker(stack, engine_state))?;
|
2022-07-24 16:01:59 +02:00
|
|
|
if let Some(cwd) = stack.get_env_var(engine_state, "PWD") {
|
|
|
|
let path = cwd.as_string()?;
|
2022-09-13 14:36:53 +02:00
|
|
|
|
|
|
|
// Communicate the path as OSC 7 (often used for spawning new tabs in the same dir)
|
|
|
|
run_ansi_sequence(&format!(
|
|
|
|
"\x1b]7;file://{}{}{}\x1b\\",
|
|
|
|
percent_encoding::utf8_percent_encode(
|
|
|
|
&hostname.unwrap_or_else(|| "localhost".to_string()),
|
|
|
|
percent_encoding::CONTROLS
|
|
|
|
),
|
|
|
|
if path.starts_with('/') { "" } else { "/" },
|
|
|
|
percent_encoding::utf8_percent_encode(
|
|
|
|
&path,
|
|
|
|
percent_encoding::CONTROLS
|
|
|
|
)
|
|
|
|
))?;
|
|
|
|
|
2022-07-24 16:01:59 +02:00
|
|
|
// Try to abbreviate string for windows title
|
|
|
|
let maybe_abbrev_path = if let Some(p) = nu_path::home_dir() {
|
|
|
|
path.replace(&p.as_path().display().to_string(), "~")
|
|
|
|
} else {
|
|
|
|
path
|
|
|
|
};
|
|
|
|
|
|
|
|
// Set window title too
|
|
|
|
// https://tldp.org/HOWTO/Xterm-Title-3.html
|
|
|
|
// ESC]0;stringBEL -- Set icon name and window title to string
|
|
|
|
// ESC]1;stringBEL -- Set icon name to string
|
|
|
|
// ESC]2;stringBEL -- Set window title to string
|
2023-01-30 02:37:54 +01:00
|
|
|
run_ansi_sequence(&format!("\x1b]2;{maybe_abbrev_path}\x07"))?;
|
2022-07-24 16:01:59 +02:00
|
|
|
}
|
2022-07-29 15:47:31 +02:00
|
|
|
run_ansi_sequence(RESET_APPLICATION_MODE)?;
|
2022-06-03 00:57:19 +02:00
|
|
|
}
|
2022-09-09 22:31:32 +02:00
|
|
|
|
2023-06-11 00:38:11 +02:00
|
|
|
let mut repl = engine_state.repl_state.lock().expect("repl state mutex");
|
2023-03-16 23:45:35 +01:00
|
|
|
line_editor.run_edit_commands(&[
|
|
|
|
EditCommand::Clear,
|
2023-06-11 00:38:11 +02:00
|
|
|
EditCommand::InsertString(repl.buffer.to_string()),
|
|
|
|
EditCommand::MoveToPosition(repl.cursor_pos),
|
2023-03-16 23:45:35 +01:00
|
|
|
]);
|
2023-06-11 00:38:11 +02:00
|
|
|
repl.buffer = "".to_string();
|
|
|
|
repl.cursor_pos = 0;
|
|
|
|
drop(repl);
|
2022-01-18 09:48:28 +01:00
|
|
|
}
|
|
|
|
Ok(Signal::CtrlC) => {
|
|
|
|
// `Reedline` clears the line content. New prompt is shown
|
2022-06-03 00:57:19 +02:00
|
|
|
if shell_integration {
|
2022-07-20 22:03:29 +02:00
|
|
|
run_ansi_sequence(&get_command_finished_marker(stack, engine_state))?;
|
2022-06-03 00:57:19 +02:00
|
|
|
}
|
2022-01-18 09:48:28 +01:00
|
|
|
}
|
|
|
|
Ok(Signal::CtrlD) => {
|
|
|
|
// When exiting clear to a new line
|
2022-06-03 00:57:19 +02:00
|
|
|
if shell_integration {
|
2022-07-20 22:03:29 +02:00
|
|
|
run_ansi_sequence(&get_command_finished_marker(stack, engine_state))?;
|
2022-06-03 00:57:19 +02:00
|
|
|
}
|
2022-01-18 09:48:28 +01:00
|
|
|
println!();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
Err(err) => {
|
|
|
|
let message = err.to_string();
|
|
|
|
if !message.contains("duration") {
|
2023-01-30 02:37:54 +01:00
|
|
|
eprintln!("Error: {err:?}");
|
2022-09-05 13:33:54 +02:00
|
|
|
// TODO: Identify possible error cases where a hard failure is preferable
|
|
|
|
// Ignoring and reporting could hide bigger problems
|
|
|
|
// e.g. https://github.com/nushell/nushell/issues/6452
|
|
|
|
// Alternatively only allow that expected failures let the REPL loop
|
2022-01-18 09:48:28 +01:00
|
|
|
}
|
2022-06-03 00:57:19 +02:00
|
|
|
if shell_integration {
|
2022-07-20 22:03:29 +02:00
|
|
|
run_ansi_sequence(&get_command_finished_marker(stack, engine_state))?;
|
2022-06-03 00:57:19 +02:00
|
|
|
}
|
2022-01-18 09:48:28 +01:00
|
|
|
}
|
|
|
|
}
|
2023-01-24 21:28:59 +01:00
|
|
|
perf(
|
|
|
|
"processing line editor input",
|
|
|
|
start_time,
|
|
|
|
file!(),
|
|
|
|
line!(),
|
|
|
|
column!(),
|
2023-02-02 00:03:05 +01:00
|
|
|
use_color,
|
2023-01-24 21:28:59 +01:00
|
|
|
);
|
|
|
|
|
|
|
|
perf(
|
|
|
|
"finished repl loop",
|
|
|
|
loop_start_time,
|
|
|
|
file!(),
|
|
|
|
line!(),
|
|
|
|
column!(),
|
2023-02-02 00:03:05 +01:00
|
|
|
use_color,
|
2023-01-24 21:28:59 +01:00
|
|
|
);
|
2022-01-18 09:48:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2022-05-08 21:28:39 +02:00
|
|
|
|
2023-07-07 03:16:17 +02:00
|
|
|
fn store_history_id_in_engine(engine_state: &mut EngineState, line_editor: &Reedline) {
|
|
|
|
let session_id = line_editor
|
|
|
|
.get_history_session_id()
|
|
|
|
.map(i64::from)
|
|
|
|
.unwrap_or(0);
|
|
|
|
|
|
|
|
engine_state.history_session_id = session_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn update_line_editor_history(
|
|
|
|
engine_state: &mut EngineState,
|
|
|
|
history_path: &Path,
|
|
|
|
line_editor: Reedline,
|
|
|
|
history_session_id: Option<HistorySessionId>,
|
|
|
|
) -> Result<Reedline, ErrReport> {
|
|
|
|
let config = engine_state.get_config();
|
|
|
|
let history: Box<dyn reedline::History> = match engine_state.config.history_file_format {
|
|
|
|
HistoryFileFormat::PlainText => Box::new(
|
|
|
|
FileBackedHistory::with_file(
|
|
|
|
config.max_history_size as usize,
|
|
|
|
history_path.to_path_buf(),
|
|
|
|
)
|
|
|
|
.into_diagnostic()?,
|
|
|
|
),
|
2023-09-18 07:49:26 +02:00
|
|
|
HistoryFileFormat::Sqlite => Box::new(
|
|
|
|
SqliteBackedHistory::with_file(
|
|
|
|
history_path.to_path_buf(),
|
|
|
|
history_session_id,
|
|
|
|
Some(chrono::Utc::now()),
|
|
|
|
)
|
|
|
|
.into_diagnostic()?,
|
|
|
|
),
|
2023-07-07 03:16:17 +02:00
|
|
|
};
|
|
|
|
let line_editor = line_editor
|
|
|
|
.with_history_session_id(history_session_id)
|
|
|
|
.with_history_exclusion_prefix(Some(" ".into()))
|
|
|
|
.with_history(history);
|
|
|
|
|
|
|
|
store_history_id_in_engine(engine_state, &line_editor);
|
|
|
|
|
|
|
|
Ok(line_editor)
|
|
|
|
}
|
|
|
|
|
2023-04-14 22:14:57 +02:00
|
|
|
fn map_nucursorshape_to_cursorshape(shape: NuCursorShape) -> SetCursorStyle {
|
2023-01-13 21:37:39 +01:00
|
|
|
match shape {
|
2023-04-14 22:14:57 +02:00
|
|
|
NuCursorShape::Block => SetCursorStyle::SteadyBlock,
|
support blink cursor, and fix underscore's cursorshape (#8990)
# Description
Close: #8988
Thanks to new crossterm version, nushell can support blink cursor shape.
It can be config with the following value:
1. blink_block
2. blink_line
3. blink_underscore
And original block, line, underscore will be steady. It also fixes wrong
shape of `underscore`.
# User-Facing Changes
Here is a little breaking change, before the change: `line` cursor shape
is blinking line, but after this pr, it will be `steady line`. To make a
blink line, we need to change the value to `blink_line`.
But I think it's ok, because after the change, we have a good naming
convention about the name of shape
# 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
- `cargo run -- crates/nu-std/tests/run.nu` 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-04-26 15:14:50 +02:00
|
|
|
NuCursorShape::UnderScore => SetCursorStyle::SteadyUnderScore,
|
|
|
|
NuCursorShape::Line => SetCursorStyle::SteadyBar,
|
|
|
|
NuCursorShape::BlinkBlock => SetCursorStyle::BlinkingBlock,
|
|
|
|
NuCursorShape::BlinkUnderScore => SetCursorStyle::BlinkingUnderScore,
|
|
|
|
NuCursorShape::BlinkLine => SetCursorStyle::BlinkingBar,
|
2023-01-13 21:37:39 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-20 22:03:29 +02:00
|
|
|
pub fn get_command_finished_marker(stack: &Stack, engine_state: &EngineState) -> String {
|
|
|
|
let exit_code = stack
|
|
|
|
.get_env_var(engine_state, "LAST_EXIT_CODE")
|
|
|
|
.and_then(|e| e.as_i64().ok());
|
|
|
|
|
|
|
|
format!("\x1b]133;D;{}\x1b\\", exit_code.unwrap_or(0))
|
|
|
|
}
|
|
|
|
|
2022-07-10 12:45:46 +02:00
|
|
|
fn run_ansi_sequence(seq: &str) -> Result<(), ShellError> {
|
2023-01-24 12:23:42 +01:00
|
|
|
io::stdout().write_all(seq.as_bytes()).map_err(|e| {
|
|
|
|
ShellError::GenericError(
|
|
|
|
"Error writing ansi sequence".into(),
|
|
|
|
e.to_string(),
|
|
|
|
Some(Span::unknown()),
|
|
|
|
None,
|
|
|
|
Vec::new(),
|
|
|
|
)
|
|
|
|
})?;
|
2022-07-10 12:45:46 +02:00
|
|
|
io::stdout().flush().map_err(|e| {
|
|
|
|
ShellError::GenericError(
|
|
|
|
"Error flushing stdio".into(),
|
|
|
|
e.to_string(),
|
2022-12-03 10:44:12 +01:00
|
|
|
Some(Span::unknown()),
|
2022-07-10 12:45:46 +02:00
|
|
|
None,
|
|
|
|
Vec::new(),
|
|
|
|
)
|
|
|
|
})
|
|
|
|
}
|
2022-07-16 04:01:38 +02:00
|
|
|
|
2022-12-17 19:30:04 +01:00
|
|
|
// Absolute paths with a drive letter, like 'C:', 'D:\', 'E:\foo'
|
|
|
|
#[cfg(windows)]
|
|
|
|
static DRIVE_PATH_REGEX: once_cell::sync::Lazy<fancy_regex::Regex> =
|
|
|
|
once_cell::sync::Lazy::new(|| {
|
|
|
|
fancy_regex::Regex::new(r"^[a-zA-Z]:[/\\]?").expect("Internal error: regex creation")
|
|
|
|
});
|
2022-07-16 04:01:38 +02:00
|
|
|
|
|
|
|
// A best-effort "does this string look kinda like a path?" function to determine whether to auto-cd
|
|
|
|
fn looks_like_path(orig: &str) -> bool {
|
|
|
|
#[cfg(windows)]
|
|
|
|
{
|
2022-08-04 21:51:02 +02:00
|
|
|
if DRIVE_PATH_REGEX.is_match(orig).unwrap_or(false) {
|
2022-07-16 04:01:38 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
orig.starts_with('.')
|
|
|
|
|| orig.starts_with('~')
|
|
|
|
|| orig.starts_with('/')
|
|
|
|
|| orig.starts_with('\\')
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn looks_like_path_windows_drive_path_works() {
|
|
|
|
let on_windows = cfg!(windows);
|
|
|
|
assert_eq!(looks_like_path("C:"), on_windows);
|
|
|
|
assert_eq!(looks_like_path("D:\\"), on_windows);
|
|
|
|
assert_eq!(looks_like_path("E:/"), on_windows);
|
|
|
|
assert_eq!(looks_like_path("F:\\some_dir"), on_windows);
|
|
|
|
assert_eq!(looks_like_path("G:/some_dir"), on_windows);
|
|
|
|
}
|
2023-07-07 03:16:17 +02:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn are_session_ids_in_sync() {
|
|
|
|
let engine_state = &mut EngineState::new();
|
|
|
|
let history_path_o =
|
|
|
|
crate::config_files::get_history_path("nushell", engine_state.config.history_file_format);
|
|
|
|
assert!(history_path_o.is_some());
|
|
|
|
let history_path = history_path_o.as_deref().unwrap();
|
|
|
|
let line_editor = reedline::Reedline::create();
|
|
|
|
let history_session_id = reedline::Reedline::create_history_session_id();
|
|
|
|
let line_editor =
|
|
|
|
update_line_editor_history(engine_state, history_path, line_editor, history_session_id);
|
|
|
|
assert_eq!(
|
|
|
|
i64::from(line_editor.unwrap().get_history_session_id().unwrap()),
|
|
|
|
engine_state.history_session_id
|
|
|
|
);
|
|
|
|
}
|