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},
|
2022-03-16 19:17:06 +01:00
|
|
|
util::{eval_source, report_error},
|
2022-05-05 17:10:03 +02:00
|
|
|
NuHighlighter, NuValidator, NushellPrompt,
|
2022-01-18 20:32:45 +01:00
|
|
|
};
|
2022-05-05 17:10:03 +02:00
|
|
|
use log::{info, trace};
|
2022-01-18 09:48:28 +01:00
|
|
|
use miette::{IntoDiagnostic, Result};
|
|
|
|
use nu_color_config::get_color_config;
|
2022-05-08 21:28:39 +02:00
|
|
|
use nu_engine::{convert_env_values, eval_block};
|
2022-01-18 09:48:28 +01:00
|
|
|
use nu_parser::lex;
|
|
|
|
use nu_protocol::{
|
2022-05-05 17:10:03 +02:00
|
|
|
engine::{EngineState, Stack, StateWorkingSet},
|
2022-06-14 22:53:33 +02:00
|
|
|
BlockId, HistoryFileFormat, PipelineData, PositionalArg, ShellError, Span, Value,
|
2022-01-18 09:48:28 +01:00
|
|
|
};
|
2022-06-14 22:53:33 +02:00
|
|
|
use reedline::{DefaultHinter, Emacs, SqliteBackedHistory, Vi};
|
2022-04-24 02:53:12 +02:00
|
|
|
use std::io::{self, Write};
|
2022-02-09 23:08:16 +01:00
|
|
|
use std::{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-06-03 00:57:19 +02:00
|
|
|
const PRE_PROMPT_MARKER: &str = "\x1b]133;A\x1b\\";
|
|
|
|
const PRE_EXECUTE_MARKER: &str = "\x1b]133;C\x1b\\";
|
|
|
|
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-03-16 19:17:06 +01:00
|
|
|
is_perf_true: bool,
|
2022-02-19 21:54:43 +01:00
|
|
|
) -> Result<()> {
|
2022-01-18 09:48:28 +01:00
|
|
|
use reedline::{FileBackedHistory, Reedline, Signal};
|
|
|
|
|
|
|
|
let mut entry_num = 0;
|
|
|
|
|
|
|
|
let mut nu_prompt = NushellPrompt::new();
|
|
|
|
|
2022-03-16 19:17:06 +01:00
|
|
|
if is_perf_true {
|
2022-02-10 22:22:39 +01:00
|
|
|
info!(
|
|
|
|
"translate environment vars {}:{}:{}",
|
|
|
|
file!(),
|
|
|
|
line!(),
|
|
|
|
column!()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
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(),
|
|
|
|
Value::String {
|
|
|
|
val: "0823".to_string(),
|
|
|
|
span: Span { start: 0, end: 0 },
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
2022-02-26 14:57:45 +01:00
|
|
|
stack.add_env_var(
|
|
|
|
"LAST_EXIT_CODE".into(),
|
|
|
|
Value::Int {
|
|
|
|
val: 0,
|
|
|
|
span: Span { start: 0, end: 0 },
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
2022-03-31 23:25:48 +02:00
|
|
|
if is_perf_true {
|
|
|
|
info!(
|
|
|
|
"load config initially {}:{}:{}",
|
|
|
|
file!(),
|
|
|
|
line!(),
|
|
|
|
column!()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the config once for the history `max_history_size`
|
|
|
|
// Updating that will not be possible in one session
|
2022-04-19 00:28:01 +02:00
|
|
|
let mut config = engine_state.get_config();
|
2022-03-31 23:25:48 +02:00
|
|
|
|
|
|
|
if is_perf_true {
|
|
|
|
info!("setup reedline {}:{}:{}", file!(), line!(), column!());
|
|
|
|
}
|
2022-04-01 00:16:28 +02:00
|
|
|
let mut line_editor = Reedline::create();
|
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() {
|
|
|
|
if is_perf_true {
|
|
|
|
info!("setup history {}:{}:{}", file!(), line!(), column!());
|
|
|
|
}
|
2022-06-14 22:53:33 +02:00
|
|
|
|
|
|
|
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()?,
|
|
|
|
),
|
|
|
|
HistoryFileFormat::Sqlite => Box::new(
|
|
|
|
SqliteBackedHistory::with_file(history_path.to_path_buf()).into_diagnostic()?,
|
|
|
|
),
|
|
|
|
};
|
2022-04-01 00:16:28 +02:00
|
|
|
line_editor = line_editor.with_history(history);
|
2022-03-31 23:25:48 +02:00
|
|
|
};
|
|
|
|
|
2022-06-14 22:53:33 +02:00
|
|
|
let sys = sysinfo::System::new();
|
|
|
|
|
2022-01-18 09:48:28 +01:00
|
|
|
loop {
|
2022-03-16 19:17:06 +01:00
|
|
|
if is_perf_true {
|
2022-02-10 22:22:39 +01:00
|
|
|
info!(
|
|
|
|
"load config each loop {}:{}:{}",
|
|
|
|
file!(),
|
|
|
|
line!(),
|
|
|
|
column!()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
}
|
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);
|
|
|
|
}
|
2022-01-18 09:48:28 +01:00
|
|
|
|
2022-04-19 00:28:01 +02:00
|
|
|
config = engine_state.get_config();
|
2022-04-11 20:19:42 +02:00
|
|
|
|
|
|
|
if is_perf_true {
|
|
|
|
info!("setup colors {}:{}:{}", file!(), line!(), column!());
|
|
|
|
}
|
|
|
|
|
2022-04-19 00:28:01 +02:00
|
|
|
let color_hm = get_color_config(config);
|
2022-01-18 09:48:28 +01:00
|
|
|
|
2022-04-11 20:19:42 +02:00
|
|
|
if is_perf_true {
|
|
|
|
info!("update reedline {}:{}:{}", file!(), line!(), column!());
|
|
|
|
}
|
2022-04-06 14:25:02 +02:00
|
|
|
let engine_reference = std::sync::Arc::new(engine_state.clone());
|
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 {
|
|
|
|
engine_state: engine_state.clone(),
|
|
|
|
config: config.clone(),
|
|
|
|
}))
|
|
|
|
.with_animation(config.animate_prompt)
|
|
|
|
.with_validator(Box::new(NuValidator {
|
|
|
|
engine_state: engine_state.clone(),
|
|
|
|
}))
|
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)
|
2022-01-27 08:53:23 +01:00
|
|
|
.with_ansi_colors(config.use_ansi_coloring);
|
|
|
|
|
2022-04-11 20:19:42 +02:00
|
|
|
line_editor = if config.use_ansi_coloring {
|
|
|
|
line_editor.with_hinter(Box::new(
|
|
|
|
DefaultHinter::default().with_style(color_hm["hints"]),
|
|
|
|
))
|
|
|
|
} else {
|
|
|
|
line_editor.disable_hints()
|
|
|
|
};
|
2022-02-10 22:22:39 +01:00
|
|
|
|
2022-04-19 00:28:01 +02:00
|
|
|
line_editor = match add_menus(line_editor, engine_reference, stack, config) {
|
2022-04-04 16:54:48 +02:00
|
|
|
Ok(line_editor) => line_editor,
|
|
|
|
Err(e) => {
|
|
|
|
let working_set = StateWorkingSet::new(engine_state);
|
|
|
|
report_error(&working_set, &e);
|
|
|
|
Reedline::create()
|
|
|
|
}
|
|
|
|
};
|
2022-03-27 15:01:04 +02:00
|
|
|
|
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
|
|
|
|
};
|
2022-04-30 16:40:41 +02:00
|
|
|
|
2022-03-31 23:25:48 +02:00
|
|
|
if config.sync_history_on_enter {
|
|
|
|
if is_perf_true {
|
|
|
|
info!("sync history {}:{}:{}", file!(), line!(), column!());
|
2022-01-18 09:48:28 +01:00
|
|
|
}
|
2022-03-31 23:25:48 +02:00
|
|
|
line_editor.sync_history().into_diagnostic()?;
|
|
|
|
}
|
2022-01-18 09:48:28 +01:00
|
|
|
|
2022-03-16 19:17:06 +01:00
|
|
|
if is_perf_true {
|
2022-02-10 22:22:39 +01:00
|
|
|
info!("setup keybindings {}:{}:{}", file!(), line!(), column!());
|
|
|
|
}
|
|
|
|
|
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
|
|
|
};
|
|
|
|
|
2022-03-16 19:17:06 +01:00
|
|
|
if is_perf_true {
|
2022-02-10 22:22:39 +01:00
|
|
|
info!("prompt_update {}:{}:{}", file!(), line!(), column!());
|
2022-02-09 23:08:16 +01:00
|
|
|
}
|
|
|
|
|
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
|
|
|
|
if let Some(hook) = &config.hooks.pre_prompt {
|
2022-05-20 23:49:42 +02:00
|
|
|
if let Err(err) = run_hook(engine_state, stack, vec![], hook) {
|
2022-05-09 03:56:48 +02:00
|
|
|
let working_set = StateWorkingSet::new(engine_state);
|
|
|
|
report_error(&working_set, &err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-20 23:49:42 +02:00
|
|
|
// Next, check all the environment variables they ask for
|
|
|
|
// fire the "env_change" hook
|
|
|
|
if let Some(hook) = config.hooks.env_change.clone() {
|
|
|
|
match hook {
|
|
|
|
Value::Record {
|
|
|
|
cols, vals: blocks, ..
|
|
|
|
} => {
|
|
|
|
for (idx, env_var) in cols.iter().enumerate() {
|
|
|
|
let before = engine_state
|
|
|
|
.previous_env_vars
|
|
|
|
.get(env_var)
|
|
|
|
.cloned()
|
|
|
|
.unwrap_or_default();
|
|
|
|
let after = stack.get_env_var(engine_state, env_var).unwrap_or_default();
|
|
|
|
if before != after {
|
|
|
|
if let Err(err) = run_hook(
|
|
|
|
engine_state,
|
|
|
|
stack,
|
|
|
|
vec![before, after.clone()],
|
|
|
|
&blocks[idx],
|
|
|
|
) {
|
|
|
|
let working_set = StateWorkingSet::new(engine_state);
|
|
|
|
report_error(&working_set, &err);
|
|
|
|
}
|
|
|
|
|
|
|
|
engine_state
|
|
|
|
.previous_env_vars
|
|
|
|
.insert(env_var.to_string(), after);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
x => {
|
|
|
|
let working_set = StateWorkingSet::new(engine_state);
|
|
|
|
report_error(
|
|
|
|
&working_set,
|
|
|
|
&ShellError::TypeMismatch(
|
|
|
|
"record for 'env_change' hook".to_string(),
|
|
|
|
x.span().unwrap_or_else(|_| Span::new(0, 0)),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
config = engine_state.get_config();
|
|
|
|
|
2022-06-03 00:57:19 +02:00
|
|
|
let shell_integration = config.shell_integration;
|
|
|
|
if shell_integration {
|
|
|
|
run_ansi_sequence(PRE_PROMPT_MARKER)?;
|
2022-05-10 23:33:18 +02:00
|
|
|
}
|
|
|
|
|
2022-04-19 00:28:01 +02:00
|
|
|
let prompt =
|
|
|
|
prompt_update::update_prompt(config, engine_state, stack, &mut nu_prompt, is_perf_true);
|
2022-01-18 09:48:28 +01:00
|
|
|
|
2022-02-10 22:22:39 +01:00
|
|
|
entry_num += 1;
|
|
|
|
|
2022-03-16 19:17:06 +01:00
|
|
|
if is_perf_true {
|
2022-02-09 23:08:16 +01:00
|
|
|
info!(
|
2022-02-10 22:22:39 +01:00
|
|
|
"finished setup, starting repl {}:{}:{}",
|
2022-02-09 23:08:16 +01:00
|
|
|
file!(),
|
|
|
|
line!(),
|
|
|
|
column!()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-01-18 09:48:28 +01:00
|
|
|
let input = line_editor.read_line(prompt);
|
2022-04-17 05:03:02 +02:00
|
|
|
|
2022-01-18 09:48:28 +01:00
|
|
|
match input {
|
|
|
|
Ok(Signal::Success(s)) => {
|
2022-06-14 22:53:33 +02:00
|
|
|
let history_supports_meta =
|
|
|
|
matches!(config.history_file_format, HistoryFileFormat::Sqlite);
|
|
|
|
if history_supports_meta && !s.is_empty() {
|
|
|
|
line_editor
|
|
|
|
.update_last_command_context(&|mut c| {
|
|
|
|
c.start_timestamp = Some(chrono::Utc::now());
|
|
|
|
c.hostname = sys.host_name();
|
|
|
|
|
|
|
|
c.cwd = Some(StateWorkingSet::new(engine_state).get_cwd());
|
|
|
|
c
|
|
|
|
})
|
|
|
|
.into_diagnostic()?; // todo: don't stop repl if error here?
|
|
|
|
}
|
|
|
|
|
2022-05-08 21:28:39 +02:00
|
|
|
// Right before we start running the code the user gave us,
|
|
|
|
// fire the "pre_execution" hook
|
|
|
|
if let Some(hook) = &config.hooks.pre_execution {
|
2022-05-20 23:49:42 +02:00
|
|
|
if let Err(err) = run_hook(engine_state, stack, vec![], hook) {
|
2022-05-08 21:28:39 +02:00
|
|
|
let working_set = StateWorkingSet::new(engine_state);
|
|
|
|
report_error(&working_set, &err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-03 00:57:19 +02:00
|
|
|
if shell_integration {
|
2022-05-10 23:33:18 +02:00
|
|
|
run_ansi_sequence(RESET_APPLICATION_MODE)?;
|
2022-06-03 00:57:19 +02:00
|
|
|
run_ansi_sequence(PRE_EXECUTE_MARKER)?;
|
2022-05-10 23:33:18 +02:00
|
|
|
if let Some(cwd) = stack.get_env_var(engine_state, "PWD") {
|
|
|
|
let path = cwd.as_string()?;
|
|
|
|
// 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
|
|
|
|
run_ansi_sequence(&format!("\x1b]2;{}\x07", maybe_abbrev_path))?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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
|
|
|
let path = nu_path::expand_path_with(&s, &cwd);
|
|
|
|
|
|
|
|
let orig = s.clone();
|
|
|
|
|
|
|
|
if (orig.starts_with('.')
|
|
|
|
|| orig.starts_with('~')
|
|
|
|
|| orig.starts_with('/')
|
|
|
|
|| orig.starts_with('\\'))
|
|
|
|
&& path.is_dir()
|
|
|
|
&& tokens.0.len() == 1
|
|
|
|
{
|
|
|
|
// 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)
|
|
|
|
};
|
|
|
|
|
|
|
|
//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
|
|
|
|
stack.add_env_var(
|
|
|
|
"PWD".into(),
|
|
|
|
Value::String {
|
|
|
|
val: path.clone(),
|
|
|
|
span: Span { start: 0, end: 0 },
|
|
|
|
},
|
|
|
|
);
|
|
|
|
let cwd = Value::String { val: cwd, span };
|
|
|
|
|
|
|
|
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 {
|
|
|
|
v.as_integer().unwrap_or_default() as usize
|
|
|
|
} else {
|
|
|
|
0
|
|
|
|
};
|
|
|
|
|
|
|
|
shells[current_shell] = Value::String { val: path, span };
|
|
|
|
|
|
|
|
stack.add_env_var("NUSHELL_SHELLS".into(), Value::List { vals: shells, span });
|
|
|
|
} else {
|
|
|
|
trace!("eval source: {}", s);
|
|
|
|
|
|
|
|
eval_source(
|
|
|
|
engine_state,
|
2022-03-16 19:17:06 +01:00
|
|
|
stack,
|
2022-02-14 16:53:48 +01:00
|
|
|
s.as_bytes(),
|
2022-01-18 09:48:28 +01:00
|
|
|
&format!("entry #{}", entry_num),
|
2022-02-14 16:53:48 +01:00
|
|
|
PipelineData::new(Span::new(0, 0)),
|
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(),
|
|
|
|
Value::String {
|
2022-06-14 22:53:33 +02:00
|
|
|
val: format!("{}", cmd_duration.as_millis()),
|
2022-05-05 17:10:03 +02:00
|
|
|
span: Span { start: 0, end: 0 },
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
2022-01-18 09:48:28 +01:00
|
|
|
// FIXME: permanent state changes like this hopefully in time can be removed
|
|
|
|
// and be replaced by just passing the cwd in where needed
|
|
|
|
if let Some(cwd) = stack.get_env_var(engine_state, "PWD") {
|
|
|
|
let path = cwd.as_string()?;
|
|
|
|
let _ = std::env::set_current_dir(path);
|
2022-05-07 21:39:22 +02:00
|
|
|
engine_state.add_env_var("PWD".into(), cwd);
|
2022-01-18 09:48:28 +01:00
|
|
|
}
|
2022-06-03 00:57:19 +02:00
|
|
|
|
2022-06-14 22:53:33 +02:00
|
|
|
if history_supports_meta && !s.is_empty() {
|
|
|
|
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 {
|
|
|
|
// FIXME: use variant with exit code, if apropriate
|
|
|
|
run_ansi_sequence(CMD_FINISHED_MARKER)?;
|
|
|
|
}
|
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 {
|
|
|
|
run_ansi_sequence(CMD_FINISHED_MARKER)?;
|
|
|
|
}
|
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 {
|
|
|
|
run_ansi_sequence(CMD_FINISHED_MARKER)?;
|
|
|
|
}
|
2022-01-18 09:48:28 +01:00
|
|
|
println!();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
Err(err) => {
|
|
|
|
let message = err.to_string();
|
|
|
|
if !message.contains("duration") {
|
|
|
|
println!("Error: {:?}", err);
|
|
|
|
}
|
2022-06-03 00:57:19 +02:00
|
|
|
if shell_integration {
|
|
|
|
run_ansi_sequence(CMD_FINISHED_MARKER)?;
|
|
|
|
}
|
2022-01-18 09:48:28 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2022-05-08 21:28:39 +02:00
|
|
|
|
2022-05-10 23:33:18 +02:00
|
|
|
fn run_ansi_sequence(seq: &str) -> Result<(), ShellError> {
|
|
|
|
match io::stdout().write_all(seq.as_bytes()) {
|
|
|
|
Ok(it) => it,
|
|
|
|
Err(err) => {
|
|
|
|
return Err(ShellError::GenericError(
|
|
|
|
"Error writing ansi sequence".into(),
|
|
|
|
err.to_string(),
|
|
|
|
Some(Span { start: 0, end: 0 }),
|
|
|
|
None,
|
|
|
|
Vec::new(),
|
|
|
|
));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
io::stdout().flush().map_err(|e| {
|
|
|
|
ShellError::GenericError(
|
|
|
|
"Error flushing stdio".into(),
|
|
|
|
e.to_string(),
|
|
|
|
Some(Span { start: 0, end: 0 }),
|
|
|
|
None,
|
|
|
|
Vec::new(),
|
|
|
|
)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-05-08 21:28:39 +02:00
|
|
|
pub fn run_hook(
|
|
|
|
engine_state: &EngineState,
|
|
|
|
stack: &mut Stack,
|
2022-05-20 23:49:42 +02:00
|
|
|
arguments: Vec<Value>,
|
2022-05-08 21:28:39 +02:00
|
|
|
value: &Value,
|
|
|
|
) -> Result<(), ShellError> {
|
|
|
|
match value {
|
2022-05-09 03:56:48 +02:00
|
|
|
Value::List { vals, .. } => {
|
|
|
|
for val in vals {
|
2022-05-20 23:49:42 +02:00
|
|
|
run_hook(engine_state, stack, arguments.clone(), val)?
|
2022-05-09 03:56:48 +02:00
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
2022-05-08 21:28:39 +02:00
|
|
|
Value::Block {
|
|
|
|
val: block_id,
|
|
|
|
span,
|
|
|
|
..
|
2022-05-20 23:49:42 +02:00
|
|
|
} => run_hook_block(engine_state, stack, *block_id, arguments, *span),
|
2022-05-08 21:28:39 +02:00
|
|
|
x => match x.span() {
|
|
|
|
Ok(span) => Err(ShellError::MissingConfigValue(
|
|
|
|
"block for hook in config".into(),
|
|
|
|
span,
|
|
|
|
)),
|
|
|
|
_ => Err(ShellError::MissingConfigValue(
|
|
|
|
"block for hook in config".into(),
|
|
|
|
Span { start: 0, end: 0 },
|
|
|
|
)),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
2022-05-09 03:56:48 +02:00
|
|
|
|
|
|
|
pub fn run_hook_block(
|
|
|
|
engine_state: &EngineState,
|
|
|
|
stack: &mut Stack,
|
|
|
|
block_id: BlockId,
|
2022-05-20 23:49:42 +02:00
|
|
|
arguments: Vec<Value>,
|
2022-05-09 03:56:48 +02:00
|
|
|
span: Span,
|
|
|
|
) -> Result<(), ShellError> {
|
|
|
|
let block = engine_state.get_block(block_id);
|
|
|
|
let input = PipelineData::new(span);
|
|
|
|
|
2022-05-20 23:49:42 +02:00
|
|
|
let mut callee_stack = stack.gather_captures(&block.captures);
|
|
|
|
|
|
|
|
for (idx, PositionalArg { var_id, .. }) in
|
|
|
|
block.signature.required_positional.iter().enumerate()
|
|
|
|
{
|
|
|
|
if let Some(var_id) = var_id {
|
|
|
|
callee_stack.add_var(*var_id, arguments[idx].clone())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
match eval_block(engine_state, &mut callee_stack, block, input, false, false) {
|
2022-05-09 03:56:48 +02:00
|
|
|
Ok(pipeline_data) => match pipeline_data.into_value(span) {
|
|
|
|
Value::Error { error } => Err(error),
|
|
|
|
_ => Ok(()),
|
|
|
|
},
|
|
|
|
Err(err) => Err(err),
|
|
|
|
}
|
|
|
|
}
|