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-06-23 21:23:08 +02:00
|
|
|
use nu_cmd_base::util::get_guaranteed_cwd;
|
2023-09-29 16:36:03 +02:00
|
|
|
use nu_cmd_base::{hook::eval_hook, util::get_editor};
|
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::{
|
2023-10-20 11:21:58 +02:00
|
|
|
CursorConfig, CwdAwareHinter, EditCommand, Emacs, FileBackedHistory, HistorySessionId,
|
|
|
|
Reedline, SqliteBackedHistory, Vi,
|
2023-07-07 03:16:17 +02:00
|
|
|
};
|
2022-09-19 16:28:36 +02:00
|
|
|
use std::{
|
2023-09-29 16:36:03 +02:00
|
|
|
env::temp_dir,
|
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();
|
2023-09-29 16:36:03 +02:00
|
|
|
let temp_file = temp_dir().join(format!("{}.nu", uuid::Uuid::new_v4()));
|
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
|
|
|
|
2023-05-01 15:11:38 +02:00
|
|
|
// Setup history_isolation aka "history per session"
|
2023-11-14 20:27:14 +01:00
|
|
|
let history_isolation = engine_state.get_config().history_isolation;
|
2023-05-01 15:11:38 +02:00
|
|
|
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,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2023-11-14 20:27:14 +01:00
|
|
|
if engine_state.get_config().use_kitty_protocol && !reedline::kitty_protocol_available() {
|
|
|
|
warn!("Terminal doesn't support use_kitty_protocol config");
|
Add kitty protocol config to nushell (#10540)
# Description
Support keyboard enhancement protocol as implemented by Kitty console,
hence Kitty protocol.
This PR enables Nushell to use keybinding that is not available before,
such as Ctrl+i (that alias to Tab) or Ctrl+e (that alias to Esc, likely
I mistaken). After this PR merged and you set `use_kitty_protocol`
enabled, if your console app support Kitty protocol (WezTerm, Kitty,
etc.) you will be able to set more fine-grained keybinding.
For Colemak users, this feature is a blessing, because some Ctrl+[hjkl]
that previously unmap-able to Ctlr+[hnei] now it is.
# User-Facing Changes
This adds `use_kitty_protocol` config which defaults to false. When set
to `true`, it enables kitty protocol on the line editor when supported,
or else it warns.
---------
Co-authored-by: Stefan Holderbach <sholderbach@users.noreply.github.com>
2023-09-29 15:52:34 +02:00
|
|
|
}
|
|
|
|
|
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 {
|
2023-11-08 20:31:30 +01:00
|
|
|
vi_insert: map_nucursorshape_to_cursorshape(config.cursor_shape_vi_insert),
|
|
|
|
vi_normal: map_nucursorshape_to_cursorshape(config.cursor_shape_vi_normal),
|
|
|
|
emacs: map_nucursorshape_to_cursorshape(config.cursor_shape_emacs),
|
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
|
2023-11-14 20:27:14 +01:00
|
|
|
.use_kitty_keyboard_enhancement(config.use_kitty_protocol)
|
|
|
|
// try to enable bracketed paste
|
|
|
|
// It doesn't work on windows system: https://github.com/crossterm-rs/crossterm/issues/737
|
|
|
|
.use_bracketed_paste(cfg!(not(target_os = "windows")) && config.bracketed_paste)
|
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()));
|
2023-10-20 11:21:58 +02:00
|
|
|
CwdAwareHinter::default().with_style(style)
|
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
|
|
|
}))
|
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();
|
2023-09-29 16:36:03 +02:00
|
|
|
let buffer_editor = get_editor(engine_state, stack, Span::unknown());
|
|
|
|
|
|
|
|
line_editor = if let Ok((cmd, args)) = buffer_editor {
|
|
|
|
let mut command = std::process::Command::new(&cmd);
|
|
|
|
command.args(args).envs(
|
|
|
|
engine_state
|
|
|
|
.render_env_vars()
|
|
|
|
.into_iter()
|
|
|
|
.filter_map(|(k, v)| v.as_string().ok().map(|v| (k, v))),
|
|
|
|
);
|
|
|
|
line_editor.with_buffer_editor(command, temp_file.clone())
|
2022-05-22 19:32:52 +02:00
|
|
|
} 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,
|
2023-09-26 11:38:58 +02:00
|
|
|
&ShellError::DirectoryNotFound(
|
|
|
|
tokens.0[0].span,
|
|
|
|
path.to_string_lossy().to_string(),
|
|
|
|
),
|
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
|
|
|
);
|
|
|
|
}
|
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
|
|
|
|
2023-11-08 02:38:30 +01:00
|
|
|
// Supported escape sequences of Microsoft's Visual Studio Code (vscode)
|
|
|
|
// https://code.visualstudio.com/docs/terminal/shell-integration#_supported-escape-sequences
|
|
|
|
if stack.get_env_var(engine_state, "TERM_PROGRAM")
|
|
|
|
== Some(Value::test_string("vscode"))
|
|
|
|
{
|
|
|
|
// If we're in vscode, run their specific ansi escape sequence.
|
|
|
|
// This is helpful for ctrl+g to change directories in the terminal.
|
|
|
|
run_ansi_sequence(&format!("\x1b]633;P;Cwd={}\x1b\\", path))?;
|
|
|
|
} else {
|
|
|
|
// Otherwise, 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-09-13 14:36:53 +02:00
|
|
|
|
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-11-08 20:31:30 +01:00
|
|
|
fn map_nucursorshape_to_cursorshape(shape: NuCursorShape) -> Option<SetCursorStyle> {
|
2023-01-13 21:37:39 +01:00
|
|
|
match shape {
|
2023-11-08 20:31:30 +01:00
|
|
|
NuCursorShape::Block => Some(SetCursorStyle::SteadyBlock),
|
|
|
|
NuCursorShape::UnderScore => Some(SetCursorStyle::SteadyUnderScore),
|
|
|
|
NuCursorShape::Line => Some(SetCursorStyle::SteadyBar),
|
|
|
|
NuCursorShape::BlinkBlock => Some(SetCursorStyle::BlinkingBlock),
|
|
|
|
NuCursorShape::BlinkUnderScore => Some(SetCursorStyle::BlinkingUnderScore),
|
|
|
|
NuCursorShape::BlinkLine => Some(SetCursorStyle::BlinkingBar),
|
|
|
|
NuCursorShape::Inherit => None,
|
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('\\')
|
2023-10-02 21:14:02 +02:00
|
|
|
|| orig.ends_with(std::path::MAIN_SEPARATOR)
|
2022-07-16 04:01:38 +02:00
|
|
|
}
|
|
|
|
|
2023-10-02 21:14:02 +02:00
|
|
|
#[cfg(windows)]
|
2022-07-16 04:01:38 +02:00
|
|
|
#[test]
|
|
|
|
fn looks_like_path_windows_drive_path_works() {
|
2023-10-02 21:14:02 +02:00
|
|
|
assert!(looks_like_path("C:"));
|
|
|
|
assert!(looks_like_path("D:\\"));
|
|
|
|
assert!(looks_like_path("E:/"));
|
|
|
|
assert!(looks_like_path("F:\\some_dir"));
|
|
|
|
assert!(looks_like_path("G:/some_dir"));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(windows)]
|
|
|
|
#[test]
|
|
|
|
fn trailing_slash_looks_like_path() {
|
|
|
|
assert!(looks_like_path("foo\\"))
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(not(windows))]
|
|
|
|
#[test]
|
|
|
|
fn trailing_slash_looks_like_path() {
|
|
|
|
assert!(looks_like_path("foo/"))
|
2022-07-16 04:01:38 +02:00
|
|
|
}
|
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
|
|
|
|
);
|
|
|
|
}
|