2021-01-06 18:47:36 +01:00
|
|
|
use crate::line_editor::configure_ctrl_c;
|
2021-01-12 05:59:53 +01:00
|
|
|
use nu_command::commands::default_context::create_default_context;
|
2021-04-03 21:31:53 +02:00
|
|
|
use nu_engine::{
|
|
|
|
filesystem::filesystem_shell::FilesystemShellMode, maybe_print_errors, run_block, script,
|
|
|
|
EvaluationContext,
|
|
|
|
};
|
|
|
|
use std::error::Error;
|
2020-12-31 00:38:31 +01:00
|
|
|
|
|
|
|
#[allow(unused_imports)]
|
2021-03-12 06:20:54 +01:00
|
|
|
pub(crate) use nu_engine::script::{process_script, LineResult};
|
2020-12-31 00:38:31 +01:00
|
|
|
|
2020-09-17 08:02:30 +02:00
|
|
|
#[cfg(feature = "rustyline-support")]
|
2021-01-06 18:47:36 +01:00
|
|
|
use crate::line_editor::{
|
|
|
|
configure_rustyline_editor, convert_rustyline_result_to_string,
|
|
|
|
default_rustyline_editor_configuration, nu_line_editor_helper,
|
|
|
|
};
|
|
|
|
|
|
|
|
#[allow(unused_imports)]
|
|
|
|
use nu_data::config;
|
2021-03-31 07:52:34 +02:00
|
|
|
use nu_source::{Tag, Text};
|
2021-01-06 18:47:36 +01:00
|
|
|
use nu_stream::InputStream;
|
2021-04-03 21:31:53 +02:00
|
|
|
use std::ffi::OsString;
|
2021-01-06 18:47:36 +01:00
|
|
|
#[allow(unused_imports)]
|
|
|
|
use std::sync::atomic::Ordering;
|
|
|
|
|
|
|
|
#[cfg(feature = "rustyline-support")]
|
|
|
|
use rustyline::{self, error::ReadlineError};
|
|
|
|
|
2020-09-17 01:22:58 +02:00
|
|
|
use nu_errors::ShellError;
|
2020-12-18 08:53:49 +01:00
|
|
|
use nu_parser::ParserScope;
|
2021-04-03 21:31:53 +02:00
|
|
|
use nu_protocol::{
|
|
|
|
hir::ExternalRedirection, ConfigPath, NuScript, RunScriptOptions, UntaggedValue, Value,
|
|
|
|
};
|
2020-12-05 05:12:42 +01:00
|
|
|
|
2021-02-20 14:37:14 +01:00
|
|
|
use log::trace;
|
2019-05-24 09:29:16 +02:00
|
|
|
use std::iter::Iterator;
|
2020-12-31 00:38:31 +01:00
|
|
|
use std::path::PathBuf;
|
2019-05-23 06:30:43 +02:00
|
|
|
|
2021-03-15 08:26:30 +01:00
|
|
|
pub struct Options {
|
|
|
|
pub config: Option<OsString>,
|
|
|
|
pub stdin: bool,
|
|
|
|
pub scripts: Vec<NuScript>,
|
2021-03-31 07:52:34 +02:00
|
|
|
pub save_history: bool,
|
2021-03-15 08:26:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for Options {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self::new()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Options {
|
|
|
|
pub fn new() -> Self {
|
|
|
|
Self {
|
|
|
|
config: None,
|
|
|
|
stdin: false,
|
|
|
|
scripts: vec![],
|
2021-03-31 07:52:34 +02:00
|
|
|
save_history: true,
|
2021-03-27 06:08:03 +01:00
|
|
|
}
|
|
|
|
}
|
2021-03-15 08:26:30 +01:00
|
|
|
}
|
|
|
|
|
2020-08-27 09:57:40 +02:00
|
|
|
pub fn search_paths() -> Vec<std::path::PathBuf> {
|
2020-03-04 19:58:20 +01:00
|
|
|
use std::env;
|
2019-07-04 05:06:43 +02:00
|
|
|
|
2020-03-04 19:58:20 +01:00
|
|
|
let mut search_paths = Vec::new();
|
2019-09-13 01:49:29 +02:00
|
|
|
|
2020-03-04 19:58:20 +01:00
|
|
|
// Automatically add path `nu` is in as a search path
|
|
|
|
if let Ok(exe_path) = env::current_exe() {
|
|
|
|
if let Some(exe_dir) = exe_path.parent() {
|
|
|
|
search_paths.push(exe_dir.to_path_buf());
|
2019-09-13 01:49:29 +02:00
|
|
|
}
|
2019-08-27 03:46:38 +02:00
|
|
|
}
|
2019-07-03 19:37:09 +02:00
|
|
|
|
2020-08-18 09:00:02 +02:00
|
|
|
if let Ok(config) = nu_data::config::config(Tag::unknown()) {
|
2021-02-12 11:13:14 +01:00
|
|
|
if let Some(Value {
|
|
|
|
value: UntaggedValue::Table(pipelines),
|
|
|
|
..
|
|
|
|
}) = config.get("plugin_dirs")
|
|
|
|
{
|
|
|
|
for pipeline in pipelines {
|
|
|
|
if let Ok(plugin_dir) = pipeline.as_string() {
|
|
|
|
search_paths.push(PathBuf::from(plugin_dir));
|
2020-05-28 21:14:32 +02:00
|
|
|
}
|
2019-11-10 04:44:05 +01:00
|
|
|
}
|
|
|
|
}
|
2019-09-12 05:20:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
search_paths
|
|
|
|
}
|
|
|
|
|
2021-04-03 21:31:53 +02:00
|
|
|
pub async fn run_script_file(
|
|
|
|
options: Options,
|
|
|
|
run_options: RunScriptOptions,
|
|
|
|
) -> Result<(), Box<dyn Error>> {
|
|
|
|
let context = create_default_context(FilesystemShellMode::Script, false)?;
|
2020-04-15 19:50:35 +02:00
|
|
|
|
2021-03-31 07:52:34 +02:00
|
|
|
if let Some(cfg) = options.config {
|
|
|
|
load_cfg_as_global_cfg(&context, PathBuf::from(cfg)).await;
|
|
|
|
} else {
|
|
|
|
load_global_cfg(&context).await;
|
|
|
|
}
|
2020-09-17 01:22:58 +02:00
|
|
|
|
2021-03-31 07:52:34 +02:00
|
|
|
let _ = register_plugins(&context);
|
|
|
|
let _ = configure_ctrl_c(&context);
|
2020-04-15 19:50:35 +02:00
|
|
|
|
2021-04-03 21:31:53 +02:00
|
|
|
for script in options.scripts {
|
|
|
|
script::run_script(script, &run_options, &context).await;
|
|
|
|
}
|
2020-02-09 03:24:33 +01:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-09-17 08:02:30 +02:00
|
|
|
#[cfg(feature = "rustyline-support")]
|
2021-03-31 07:52:34 +02:00
|
|
|
pub async fn cli(context: EvaluationContext, options: Options) -> Result<(), Box<dyn Error>> {
|
|
|
|
let _ = configure_ctrl_c(&context);
|
2020-07-22 23:43:52 +02:00
|
|
|
|
2021-03-31 07:52:34 +02:00
|
|
|
// start time for running startup scripts (this metric includes loading of the cfg, but w/e)
|
2021-02-20 14:37:14 +01:00
|
|
|
let startup_commands_start_time = std::time::Instant::now();
|
2021-03-31 07:52:34 +02:00
|
|
|
|
|
|
|
if let Some(cfg) = options.config {
|
|
|
|
load_cfg_as_global_cfg(&context, PathBuf::from(cfg)).await;
|
|
|
|
} else {
|
|
|
|
load_global_cfg(&context).await;
|
|
|
|
}
|
2021-02-20 14:37:14 +01:00
|
|
|
// Store cmd duration in an env var
|
|
|
|
context.scope.add_env_var(
|
|
|
|
"CMD_DURATION",
|
|
|
|
format!("{:?}", startup_commands_start_time.elapsed()),
|
|
|
|
);
|
|
|
|
trace!(
|
|
|
|
"startup commands took {:?}",
|
|
|
|
startup_commands_start_time.elapsed()
|
|
|
|
);
|
2020-08-27 13:06:25 +02:00
|
|
|
|
2021-03-31 07:52:34 +02:00
|
|
|
//Configure rustyline
|
|
|
|
let mut rl = default_rustyline_editor_configuration();
|
|
|
|
let history_path = if let Some(cfg) = &context.configs.lock().global_config {
|
|
|
|
let _ = configure_rustyline_editor(&mut rl, cfg);
|
|
|
|
let helper = Some(nu_line_editor_helper(&context, cfg));
|
|
|
|
rl.set_helper(helper);
|
|
|
|
nu_data::config::path::history_path(cfg)
|
|
|
|
} else {
|
|
|
|
nu_data::config::path::default_history_path()
|
|
|
|
};
|
|
|
|
|
|
|
|
// Don't load history if it's not necessary
|
|
|
|
if options.save_history {
|
|
|
|
let _ = rl.load_history(&history_path);
|
|
|
|
}
|
|
|
|
|
|
|
|
//set vars from cfg if present
|
|
|
|
let (skip_welcome_message, prompt) = if let Some(cfg) = &context.configs.lock().global_config {
|
|
|
|
(
|
|
|
|
cfg.var("skip_welcome_message")
|
|
|
|
.map(|x| x.is_true())
|
|
|
|
.unwrap_or(false),
|
|
|
|
cfg.var("prompt"),
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
(false, None)
|
|
|
|
};
|
|
|
|
|
|
|
|
//Check whether dir we start in contains local cfg file and if so load it.
|
|
|
|
load_local_cfg_if_present(&context).await;
|
|
|
|
|
2020-12-18 08:53:49 +01:00
|
|
|
// Give ourselves a scope to work in
|
|
|
|
context.scope.enter_scope();
|
|
|
|
|
|
|
|
let mut session_text = String::new();
|
|
|
|
let mut line_start: usize = 0;
|
|
|
|
|
2020-08-09 01:38:21 +02:00
|
|
|
if !skip_welcome_message {
|
|
|
|
println!(
|
|
|
|
"Welcome to Nushell {} (type 'help' for more info)",
|
|
|
|
clap::crate_version!()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2019-05-26 08:54:41 +02:00
|
|
|
#[cfg(windows)]
|
|
|
|
{
|
2021-02-22 19:33:34 +01:00
|
|
|
let _ = nu_ansi_term::enable_ansi_support();
|
2019-05-26 08:54:41 +02:00
|
|
|
}
|
|
|
|
|
2019-06-15 20:36:17 +02:00
|
|
|
let mut ctrlcbreak = false;
|
2020-04-15 07:43:23 +02:00
|
|
|
|
2021-04-03 21:31:53 +02:00
|
|
|
let mut run_options = RunScriptOptions::default()
|
|
|
|
.cli_mode(true)
|
|
|
|
.redirect_stdin(false);
|
|
|
|
|
2019-05-23 06:30:43 +02:00
|
|
|
loop {
|
2019-10-13 06:12:43 +02:00
|
|
|
if context.ctrl_c.load(Ordering::SeqCst) {
|
|
|
|
context.ctrl_c.store(false, Ordering::SeqCst);
|
2019-06-07 02:31:22 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
Restructure and streamline token expansion (#1123)
Restructure and streamline token expansion
The purpose of this commit is to streamline the token expansion code, by
removing aspects of the code that are no longer relevant, removing
pointless duplication, and eliminating the need to pass the same
arguments to `expand_syntax`.
The first big-picture change in this commit is that instead of a handful
of `expand_` functions, which take a TokensIterator and ExpandContext, a
smaller number of methods on the `TokensIterator` do the same job.
The second big-picture change in this commit is fully eliminating the
coloring traits, making coloring a responsibility of the base expansion
implementations. This also means that the coloring tracer is merged into
the expansion tracer, so you can follow a single expansion and see how
the expansion process produced colored tokens.
One side effect of this change is that the expander itself is marginally
more error-correcting. The error correction works by switching from
structured expansion to `BackoffColoringMode` when an unexpected token
is found, which guarantees that all spans of the source are colored, but
may not be the most optimal error recovery strategy.
That said, because `BackoffColoringMode` only extends as far as a
closing delimiter (`)`, `]`, `}`) or pipe (`|`), it does result in
fairly granular correction strategy.
The current code still produces an `Err` (plus a complete list of
colored shapes) from the parsing process if any errors are encountered,
but this could easily be addressed now that the underlying expansion is
error-correcting.
This commit also colors any spans that are syntax errors in red, and
causes the parser to include some additional information about what
tokens were expected at any given point where an error was encountered,
so that completions and hinting could be more robust in the future.
Co-authored-by: Jonathan Turner <jonathandturner@users.noreply.github.com>
Co-authored-by: Andrés N. Robalino <andres@androbtech.com>
2020-01-21 23:45:03 +01:00
|
|
|
let cwd = context.shell_manager.path();
|
2019-08-07 19:49:11 +02:00
|
|
|
|
2019-11-16 21:02:26 +01:00
|
|
|
let colored_prompt = {
|
2021-03-31 07:52:34 +02:00
|
|
|
if let Some(prompt) = &prompt {
|
2020-06-27 00:37:31 +02:00
|
|
|
let prompt_line = prompt.as_string()?;
|
|
|
|
|
2020-12-18 08:53:49 +01:00
|
|
|
context.scope.enter_scope();
|
2021-02-15 08:14:16 +01:00
|
|
|
let (mut prompt_block, err) = nu_parser::parse(&prompt_line, 0, &context.scope);
|
|
|
|
|
|
|
|
prompt_block.set_redirect(ExternalRedirection::Stdout);
|
2020-11-09 17:27:07 +01:00
|
|
|
|
|
|
|
if err.is_some() {
|
2020-12-18 08:53:49 +01:00
|
|
|
context.scope.exit_scope();
|
|
|
|
|
2021-01-15 19:06:29 +01:00
|
|
|
format!("\x1b[32m{}{}\x1b[m> ", cwd, current_branch())
|
2020-11-09 17:27:07 +01:00
|
|
|
} else {
|
2020-12-18 08:53:49 +01:00
|
|
|
let run_result = run_block(&prompt_block, &context, InputStream::empty()).await;
|
|
|
|
context.scope.exit_scope();
|
2020-06-28 23:06:05 +02:00
|
|
|
|
2020-12-18 08:53:49 +01:00
|
|
|
match run_result {
|
2020-11-09 17:27:07 +01:00
|
|
|
Ok(result) => match result.collect_string(Tag::unknown()).await {
|
|
|
|
Ok(string_result) => {
|
|
|
|
let errors = context.get_errors();
|
2021-03-31 07:52:34 +02:00
|
|
|
maybe_print_errors(&context, Text::from(prompt_line));
|
2020-11-09 17:27:07 +01:00
|
|
|
context.clear_errors();
|
|
|
|
|
|
|
|
if !errors.is_empty() {
|
2020-06-28 23:06:05 +02:00
|
|
|
"> ".to_string()
|
2020-11-09 17:27:07 +01:00
|
|
|
} else {
|
|
|
|
string_result.item
|
2020-06-27 00:37:31 +02:00
|
|
|
}
|
2020-11-09 17:27:07 +01:00
|
|
|
}
|
2020-06-28 23:06:05 +02:00
|
|
|
Err(e) => {
|
2021-03-12 06:20:54 +01:00
|
|
|
context.host.lock().print_err(e, &Text::from(prompt_line));
|
2020-06-28 23:06:05 +02:00
|
|
|
context.clear_errors();
|
|
|
|
|
|
|
|
"> ".to_string()
|
2020-06-27 00:37:31 +02:00
|
|
|
}
|
2020-11-09 17:27:07 +01:00
|
|
|
},
|
|
|
|
Err(e) => {
|
2021-03-12 06:20:54 +01:00
|
|
|
context.host.lock().print_err(e, &Text::from(prompt_line));
|
2020-11-09 17:27:07 +01:00
|
|
|
context.clear_errors();
|
2020-06-27 00:37:31 +02:00
|
|
|
|
2020-11-09 17:27:07 +01:00
|
|
|
"> ".to_string()
|
|
|
|
}
|
2020-06-27 00:37:31 +02:00
|
|
|
}
|
|
|
|
}
|
2020-06-25 07:44:55 +02:00
|
|
|
} else {
|
2021-01-15 19:06:29 +01:00
|
|
|
format!("\x1b[32m{}{}\x1b[m> ", cwd, current_branch())
|
2019-10-08 15:47:30 +02:00
|
|
|
}
|
|
|
|
};
|
2019-11-16 21:02:26 +01:00
|
|
|
|
2021-03-04 18:22:14 +01:00
|
|
|
let prompt = {
|
|
|
|
if let Ok(bytes) = strip_ansi_escapes::strip(&colored_prompt) {
|
|
|
|
String::from_utf8_lossy(&bytes).to_string()
|
|
|
|
} else {
|
|
|
|
"> ".to_string()
|
2020-01-01 21:45:32 +01:00
|
|
|
}
|
2019-11-16 21:42:35 +01:00
|
|
|
};
|
|
|
|
|
2019-11-16 21:02:26 +01:00
|
|
|
rl.helper_mut().expect("No helper").colored_prompt = colored_prompt;
|
2019-09-18 00:21:39 +02:00
|
|
|
let mut initial_command = Some(String::new());
|
|
|
|
let mut readline = Err(ReadlineError::Eof);
|
|
|
|
while let Some(ref cmd) = initial_command {
|
2019-11-16 21:02:26 +01:00
|
|
|
readline = rl.readline_with_initial(&prompt, (&cmd, ""));
|
2019-11-22 09:25:09 +01:00
|
|
|
initial_command = None;
|
2019-09-18 00:21:39 +02:00
|
|
|
}
|
2019-05-23 06:30:43 +02:00
|
|
|
|
2020-12-18 08:53:49 +01:00
|
|
|
if let Ok(line) = &readline {
|
|
|
|
line_start = session_text.len();
|
|
|
|
session_text.push_str(line);
|
|
|
|
session_text.push('\n');
|
|
|
|
}
|
|
|
|
|
2021-02-20 14:37:14 +01:00
|
|
|
// start time for command duration
|
|
|
|
let cmd_start_time = std::time::Instant::now();
|
|
|
|
|
2020-09-17 08:02:30 +02:00
|
|
|
let line = match convert_rustyline_result_to_string(readline) {
|
2020-12-18 08:53:49 +01:00
|
|
|
LineResult::Success(_) => {
|
2021-04-03 21:31:53 +02:00
|
|
|
run_options = run_options.span_offset(line_start);
|
|
|
|
process_script(&session_text[line_start..], &run_options, &context).await
|
2020-12-18 08:53:49 +01:00
|
|
|
}
|
2020-09-17 08:02:30 +02:00
|
|
|
x => x,
|
|
|
|
};
|
2019-11-04 16:47:03 +01:00
|
|
|
|
2021-02-20 14:37:14 +01:00
|
|
|
// Store cmd duration in an env var
|
|
|
|
context
|
|
|
|
.scope
|
|
|
|
.add_env_var("CMD_DURATION", format!("{:?}", cmd_start_time.elapsed()));
|
|
|
|
|
2019-11-04 16:47:03 +01:00
|
|
|
match line {
|
2019-05-23 06:30:43 +02:00
|
|
|
LineResult::Success(line) => {
|
2021-03-31 07:52:34 +02:00
|
|
|
if options.save_history {
|
2021-03-27 06:08:03 +01:00
|
|
|
rl.add_history_entry(&line);
|
2021-03-31 07:52:34 +02:00
|
|
|
let _ = rl.save_history(&history_path);
|
|
|
|
}
|
|
|
|
maybe_print_errors(&context, Text::from(session_text.clone()));
|
2019-11-04 16:47:03 +01:00
|
|
|
}
|
|
|
|
|
2020-11-09 17:23:41 +01:00
|
|
|
LineResult::ClearHistory => {
|
2021-03-31 07:52:34 +02:00
|
|
|
if options.save_history {
|
2021-03-27 06:08:03 +01:00
|
|
|
rl.clear_history();
|
2021-03-31 07:52:34 +02:00
|
|
|
let _ = rl.save_history(&history_path);
|
|
|
|
}
|
2020-11-09 17:23:41 +01:00
|
|
|
}
|
|
|
|
|
2021-03-31 07:52:34 +02:00
|
|
|
LineResult::Error(line, err) => {
|
|
|
|
if options.save_history {
|
2021-03-27 06:08:03 +01:00
|
|
|
rl.add_history_entry(&line);
|
2021-03-31 07:52:34 +02:00
|
|
|
let _ = rl.save_history(&history_path);
|
|
|
|
}
|
2019-11-04 16:47:03 +01:00
|
|
|
|
2021-03-31 07:52:34 +02:00
|
|
|
context
|
|
|
|
.host
|
|
|
|
.lock()
|
|
|
|
.print_err(err, &Text::from(session_text.clone()));
|
|
|
|
|
|
|
|
// I am not so sure, we don't need maybe_print_errors here (as we printed an err
|
|
|
|
// above), because maybe_print_errors also clears the errors.
|
|
|
|
// TODO Analyze where above err comes from, and whether we need to clear
|
|
|
|
// context.errors here
|
|
|
|
// Or just be consistent and return errors always in context.errors...
|
|
|
|
maybe_print_errors(&context, Text::from(session_text.clone()));
|
2019-05-23 06:30:43 +02:00
|
|
|
}
|
|
|
|
|
2019-06-15 20:36:17 +02:00
|
|
|
LineResult::CtrlC => {
|
2021-03-31 07:52:34 +02:00
|
|
|
let config_ctrlc_exit = context
|
|
|
|
.configs
|
|
|
|
.lock()
|
|
|
|
.global_config
|
|
|
|
.as_ref()
|
|
|
|
.map(|cfg| cfg.var("ctrlc_exit"))
|
|
|
|
.flatten()
|
|
|
|
.map(|ctrl_c| ctrl_c.is_true())
|
2019-09-25 03:01:38 +02:00
|
|
|
.unwrap_or(false); // default behavior is to allow CTRL-C spamming similar to other shells
|
|
|
|
|
|
|
|
if !config_ctrlc_exit {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2019-06-15 20:36:17 +02:00
|
|
|
if ctrlcbreak {
|
2021-03-31 07:52:34 +02:00
|
|
|
if options.save_history {
|
|
|
|
let _ = rl.save_history(&history_path);
|
|
|
|
}
|
2019-06-15 20:36:17 +02:00
|
|
|
std::process::exit(0);
|
|
|
|
} else {
|
Restructure and streamline token expansion (#1123)
Restructure and streamline token expansion
The purpose of this commit is to streamline the token expansion code, by
removing aspects of the code that are no longer relevant, removing
pointless duplication, and eliminating the need to pass the same
arguments to `expand_syntax`.
The first big-picture change in this commit is that instead of a handful
of `expand_` functions, which take a TokensIterator and ExpandContext, a
smaller number of methods on the `TokensIterator` do the same job.
The second big-picture change in this commit is fully eliminating the
coloring traits, making coloring a responsibility of the base expansion
implementations. This also means that the coloring tracer is merged into
the expansion tracer, so you can follow a single expansion and see how
the expansion process produced colored tokens.
One side effect of this change is that the expander itself is marginally
more error-correcting. The error correction works by switching from
structured expansion to `BackoffColoringMode` when an unexpected token
is found, which guarantees that all spans of the source are colored, but
may not be the most optimal error recovery strategy.
That said, because `BackoffColoringMode` only extends as far as a
closing delimiter (`)`, `]`, `}`) or pipe (`|`), it does result in
fairly granular correction strategy.
The current code still produces an `Err` (plus a complete list of
colored shapes) from the parsing process if any errors are encountered,
but this could easily be addressed now that the underlying expansion is
error-correcting.
This commit also colors any spans that are syntax errors in red, and
causes the parser to include some additional information about what
tokens were expected at any given point where an error was encountered,
so that completions and hinting could be more robust in the future.
Co-authored-by: Jonathan Turner <jonathandturner@users.noreply.github.com>
Co-authored-by: Andrés N. Robalino <andres@androbtech.com>
2020-01-21 23:45:03 +01:00
|
|
|
context.with_host(|host| host.stdout("CTRL-C pressed (again to quit)"));
|
2019-06-15 20:36:17 +02:00
|
|
|
ctrlcbreak = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-21 09:56:10 +02:00
|
|
|
LineResult::CtrlD => {
|
|
|
|
context.shell_manager.remove_at_current();
|
|
|
|
if context.shell_manager.is_empty() {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-23 06:30:43 +02:00
|
|
|
LineResult::Break => {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2019-06-15 20:36:17 +02:00
|
|
|
ctrlcbreak = false;
|
2019-05-23 06:30:43 +02:00
|
|
|
}
|
2019-08-27 00:41:57 +02:00
|
|
|
|
|
|
|
// we are ok if we can not save history
|
2021-03-31 07:52:34 +02:00
|
|
|
if options.save_history {
|
|
|
|
let _ = rl.save_history(&history_path);
|
|
|
|
}
|
2019-05-23 06:30:43 +02:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2021-03-31 07:52:34 +02:00
|
|
|
pub async fn load_local_cfg_if_present(context: &EvaluationContext) {
|
|
|
|
trace!("Loading local cfg if present");
|
|
|
|
match config::loadable_cfg_exists_in_dir(PathBuf::from(context.shell_manager.path())) {
|
|
|
|
Ok(Some(cfg_path)) => {
|
|
|
|
if let Err(err) = context.load_config(&ConfigPath::Local(cfg_path)).await {
|
|
|
|
context.host.lock().print_err(err, &Text::from(""))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Err(e) => {
|
|
|
|
//Report error while checking for local cfg file
|
|
|
|
context.host.lock().print_err(e, &Text::from(""))
|
|
|
|
}
|
|
|
|
Ok(None) => {
|
|
|
|
//No local cfg file present in start dir
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn load_cfg_as_global_cfg(context: &EvaluationContext, path: PathBuf) {
|
|
|
|
if let Err(err) = context.load_config(&ConfigPath::Global(path.clone())).await {
|
|
|
|
context.host.lock().print_err(err, &Text::from(""));
|
|
|
|
} else {
|
|
|
|
//TODO current commands assume to find path to global cfg file under config-path
|
|
|
|
//TODO use newly introduced nuconfig::file_path instead
|
|
|
|
context.scope.add_var(
|
|
|
|
"config-path",
|
|
|
|
UntaggedValue::filepath(path).into_untagged_value(),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn load_global_cfg(context: &EvaluationContext) {
|
|
|
|
match config::default_path() {
|
|
|
|
Ok(path) => {
|
|
|
|
load_cfg_as_global_cfg(context, path).await;
|
|
|
|
}
|
|
|
|
Err(e) => {
|
|
|
|
context.host.lock().print_err(e, &Text::from(""));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn register_plugins(context: &EvaluationContext) -> Result<(), ShellError> {
|
2021-01-10 03:50:49 +01:00
|
|
|
if let Ok(plugins) = nu_engine::plugin::build_plugin::scan(search_paths()) {
|
2020-09-17 01:22:58 +02:00
|
|
|
context.add_commands(
|
|
|
|
plugins
|
|
|
|
.into_iter()
|
|
|
|
.filter(|p| !context.is_command_registered(p.name()))
|
|
|
|
.collect(),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-12-19 08:47:34 +01:00
|
|
|
pub async fn parse_and_eval(line: &str, ctx: &EvaluationContext) -> Result<String, ShellError> {
|
2020-12-18 08:53:49 +01:00
|
|
|
// FIXME: do we still need this?
|
2020-11-22 01:37:16 +01:00
|
|
|
let line = if let Some(s) = line.strip_suffix('\n') {
|
|
|
|
s
|
2020-07-18 03:59:23 +02:00
|
|
|
} else {
|
|
|
|
line
|
|
|
|
};
|
|
|
|
|
2020-12-18 08:53:49 +01:00
|
|
|
// TODO ensure the command whose examples we're testing is actually in the pipeline
|
|
|
|
ctx.scope.enter_scope();
|
|
|
|
let (classified_block, err) = nu_parser::parse(&line, 0, &ctx.scope);
|
2020-11-09 17:27:07 +01:00
|
|
|
if let Some(err) = err {
|
2020-12-18 08:53:49 +01:00
|
|
|
ctx.scope.exit_scope();
|
2020-11-09 17:27:07 +01:00
|
|
|
return Err(err.into());
|
|
|
|
}
|
2020-07-18 03:59:23 +02:00
|
|
|
|
|
|
|
let input_stream = InputStream::empty();
|
2020-12-18 08:53:49 +01:00
|
|
|
|
|
|
|
let result = run_block(&classified_block, ctx, input_stream).await;
|
|
|
|
ctx.scope.exit_scope();
|
2020-07-18 03:59:23 +02:00
|
|
|
|
2020-12-18 08:53:49 +01:00
|
|
|
result?.collect_string(Tag::unknown()).await.map(|x| x.item)
|
2020-07-18 03:59:23 +02:00
|
|
|
}
|
|
|
|
|
2021-01-15 19:06:29 +01:00
|
|
|
#[allow(dead_code)]
|
|
|
|
fn current_branch() -> String {
|
|
|
|
#[cfg(feature = "shadow-rs")]
|
|
|
|
{
|
|
|
|
Some(shadow_rs::branch())
|
|
|
|
.map(|x| x.trim().to_string())
|
|
|
|
.filter(|x| !x.is_empty())
|
|
|
|
.map(|x| format!("({})", x))
|
|
|
|
.unwrap_or_default()
|
|
|
|
}
|
|
|
|
#[cfg(not(feature = "shadow-rs"))]
|
|
|
|
{
|
|
|
|
"".to_string()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-11 21:05:59 +02:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2021-04-03 21:31:53 +02:00
|
|
|
use nu_engine::{basic_evaluation_context, filesystem::filesystem_shell::FilesystemShellMode};
|
2020-12-18 08:53:49 +01:00
|
|
|
|
2020-04-11 21:05:59 +02:00
|
|
|
#[quickcheck]
|
|
|
|
fn quickcheck_parse(data: String) -> bool {
|
2020-12-18 08:53:49 +01:00
|
|
|
let (tokens, err) = nu_parser::lex(&data, 0);
|
2021-02-12 21:31:11 +01:00
|
|
|
let (lite_block, err2) = nu_parser::parse_block(tokens);
|
2020-12-18 08:53:49 +01:00
|
|
|
if err.is_none() && err2.is_none() {
|
2021-04-03 21:31:53 +02:00
|
|
|
let context = basic_evaluation_context(FilesystemShellMode::Cli).unwrap();
|
2020-12-18 08:53:49 +01:00
|
|
|
let _ = nu_parser::classify_block(&lite_block, &context.scope);
|
2020-04-11 21:05:59 +02:00
|
|
|
}
|
2020-04-20 08:41:51 +02:00
|
|
|
true
|
2020-04-11 21:05:59 +02:00
|
|
|
}
|
|
|
|
}
|