mirror of
https://github.com/nushell/nushell
synced 2024-12-26 04:53:09 +00:00
New "display_output" hook. (#6915)
* New "display_output" hook. * Fix unrelated "clippy" complaint in nu-tables crate. * Fix code-formattng and style issues in "display_output" hook * Enhance eval_hook to return PipelineData. This allows a hook (including display_output) to return a value. Co-authored-by: JT <547158+jntrnr@users.noreply.github.com>
This commit is contained in:
parent
b90d701f89
commit
beec658872
5 changed files with 106 additions and 43 deletions
|
@ -278,7 +278,7 @@ pub fn evaluate_repl(
|
|||
// 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.clone() {
|
||||
if let Err(err) = eval_hook(engine_state, stack, vec![], &hook) {
|
||||
if let Err(err) = eval_hook(engine_state, stack, None, vec![], &hook) {
|
||||
report_error_new(engine_state, &err);
|
||||
}
|
||||
}
|
||||
|
@ -334,7 +334,7 @@ pub fn evaluate_repl(
|
|||
// Right before we start running the code the user gave us,
|
||||
// fire the "pre_execution" hook
|
||||
if let Some(hook) = config.hooks.pre_execution.clone() {
|
||||
if let Err(err) = eval_hook(engine_state, stack, vec![], &hook) {
|
||||
if let Err(err) = eval_hook(engine_state, stack, None, vec![], &hook) {
|
||||
report_error_new(engine_state, &err);
|
||||
}
|
||||
}
|
||||
|
@ -686,6 +686,7 @@ pub fn eval_env_change_hook(
|
|||
eval_hook(
|
||||
engine_state,
|
||||
stack,
|
||||
None,
|
||||
vec![("$before".into(), before), ("$after".into(), after.clone())],
|
||||
hook_value,
|
||||
)?;
|
||||
|
@ -711,15 +712,17 @@ pub fn eval_env_change_hook(
|
|||
pub fn eval_hook(
|
||||
engine_state: &mut EngineState,
|
||||
stack: &mut Stack,
|
||||
input: Option<PipelineData>,
|
||||
arguments: Vec<(String, Value)>,
|
||||
value: &Value,
|
||||
) -> Result<(), ShellError> {
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let value_span = value.span()?;
|
||||
|
||||
let condition_path = PathMember::String {
|
||||
val: "condition".to_string(),
|
||||
span: value_span,
|
||||
};
|
||||
let mut output = PipelineData::new(Span::new(0, 0));
|
||||
|
||||
let code_path = PathMember::String {
|
||||
val: "code".to_string(),
|
||||
|
@ -729,7 +732,7 @@ pub fn eval_hook(
|
|||
match value {
|
||||
Value::List { vals, .. } => {
|
||||
for val in vals {
|
||||
eval_hook(engine_state, stack, arguments.clone(), val)?
|
||||
eval_hook(engine_state, stack, None, arguments.clone(), val)?;
|
||||
}
|
||||
}
|
||||
Value::Record { .. } => {
|
||||
|
@ -745,6 +748,7 @@ pub fn eval_hook(
|
|||
engine_state,
|
||||
stack,
|
||||
block_id,
|
||||
None,
|
||||
arguments.clone(),
|
||||
block_span,
|
||||
) {
|
||||
|
@ -824,7 +828,9 @@ pub fn eval_hook(
|
|||
.collect();
|
||||
|
||||
match eval_block(engine_state, stack, &block, input, false, false) {
|
||||
Ok(_) => {}
|
||||
Ok(pipeline_data) => {
|
||||
output = pipeline_data;
|
||||
}
|
||||
Err(err) => {
|
||||
report_error_new(engine_state, &err);
|
||||
}
|
||||
|
@ -839,7 +845,14 @@ pub fn eval_hook(
|
|||
span: block_span,
|
||||
..
|
||||
} => {
|
||||
run_hook_block(engine_state, stack, block_id, arguments, block_span)?;
|
||||
run_hook_block(
|
||||
engine_state,
|
||||
stack,
|
||||
block_id,
|
||||
input,
|
||||
arguments,
|
||||
block_span,
|
||||
)?;
|
||||
}
|
||||
other => {
|
||||
return Err(ShellError::UnsupportedConfigValue(
|
||||
|
@ -856,7 +869,17 @@ pub fn eval_hook(
|
|||
span: block_span,
|
||||
..
|
||||
} => {
|
||||
run_hook_block(engine_state, stack, *block_id, arguments, *block_span)?;
|
||||
output = PipelineData::Value(
|
||||
run_hook_block(
|
||||
engine_state,
|
||||
stack,
|
||||
*block_id,
|
||||
input,
|
||||
arguments,
|
||||
*block_span,
|
||||
)?,
|
||||
None,
|
||||
);
|
||||
}
|
||||
other => {
|
||||
return Err(ShellError::UnsupportedConfigValue(
|
||||
|
@ -870,19 +893,20 @@ pub fn eval_hook(
|
|||
let cwd = get_guaranteed_cwd(engine_state, stack);
|
||||
engine_state.merge_env(stack, cwd)?;
|
||||
|
||||
Ok(())
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
pub fn run_hook_block(
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
block_id: BlockId,
|
||||
optional_input: Option<PipelineData>,
|
||||
arguments: Vec<(String, Value)>,
|
||||
span: Span,
|
||||
) -> Result<Value, ShellError> {
|
||||
let block = engine_state.get_block(block_id);
|
||||
|
||||
let input = PipelineData::new(span);
|
||||
let input = optional_input.unwrap_or_else(|| PipelineData::new(span));
|
||||
|
||||
let mut callee_stack = stack.gather_captures(&block.captures);
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
use log::trace;
|
||||
use crate::repl::eval_hook;
|
||||
use nu_engine::eval_block;
|
||||
use nu_parser::{escape_quote_string, lex, parse, unescape_unquote_string, Token, TokenContents};
|
||||
use nu_protocol::engine::StateWorkingSet;
|
||||
use nu_protocol::CliError;
|
||||
use nu_protocol::{
|
||||
engine::{EngineState, Stack},
|
||||
PipelineData, ShellError, Span, Value,
|
||||
print_if_stream, PipelineData, ShellError, Span, Value,
|
||||
};
|
||||
#[cfg(windows)]
|
||||
use nu_utils::enable_vt_processing;
|
||||
|
@ -204,8 +204,6 @@ pub fn eval_source(
|
|||
fname: &str,
|
||||
input: PipelineData,
|
||||
) -> bool {
|
||||
trace!("eval_source");
|
||||
|
||||
let (block, delta) = {
|
||||
let mut working_set = StateWorkingSet::new(engine_state);
|
||||
let (output, err) = parse(
|
||||
|
@ -232,7 +230,30 @@ pub fn eval_source(
|
|||
|
||||
match eval_block(engine_state, stack, &block, input, false, false) {
|
||||
Ok(pipeline_data) => {
|
||||
match pipeline_data.print(engine_state, stack, false, false) {
|
||||
let config = engine_state.get_config();
|
||||
let result;
|
||||
if let PipelineData::ExternalStream {
|
||||
stdout: stream,
|
||||
stderr: stderr_stream,
|
||||
exit_code,
|
||||
..
|
||||
} = pipeline_data
|
||||
{
|
||||
result = print_if_stream(stream, stderr_stream, false, exit_code);
|
||||
} else if let Some(hook) = config.hooks.display_output.clone() {
|
||||
match eval_hook(engine_state, stack, Some(pipeline_data), vec![], &hook) {
|
||||
Err(err) => {
|
||||
result = Err(err);
|
||||
}
|
||||
Ok(val) => {
|
||||
result = val.print(engine_state, stack, false, false);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result = pipeline_data.print(engine_state, stack, false, false);
|
||||
}
|
||||
|
||||
match result {
|
||||
Err(err) => {
|
||||
let working_set = StateWorkingSet::new(engine_state);
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@ pub struct Hooks {
|
|||
pub pre_prompt: Option<Value>,
|
||||
pub pre_execution: Option<Value>,
|
||||
pub env_change: Option<Value>,
|
||||
pub display_output: Option<Value>,
|
||||
}
|
||||
|
||||
impl Hooks {
|
||||
|
@ -40,6 +41,7 @@ impl Hooks {
|
|||
pre_prompt: None,
|
||||
pre_execution: None,
|
||||
env_change: None,
|
||||
display_output: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -571,9 +573,10 @@ fn create_hooks(value: &Value) -> Result<Hooks, ShellError> {
|
|||
"pre_prompt" => hooks.pre_prompt = Some(vals[idx].clone()),
|
||||
"pre_execution" => hooks.pre_execution = Some(vals[idx].clone()),
|
||||
"env_change" => hooks.env_change = Some(vals[idx].clone()),
|
||||
"display_output" => hooks.display_output = Some(vals[idx].clone()),
|
||||
x => {
|
||||
return Err(ShellError::UnsupportedConfigValue(
|
||||
"'pre_prompt', 'pre_execution', or 'env_change'".to_string(),
|
||||
"'pre_prompt', 'pre_execution', 'env_change'".to_string(),
|
||||
x.to_string(),
|
||||
*span,
|
||||
));
|
||||
|
|
|
@ -426,34 +426,13 @@ impl PipelineData {
|
|||
..
|
||||
} = self
|
||||
{
|
||||
// NOTE: currently we don't need anything from stderr
|
||||
// so directly consumes `stderr_stream` to make sure that everything is done.
|
||||
std::thread::spawn(move || stderr_stream.map(|x| x.into_bytes()));
|
||||
if let Some(stream) = stream {
|
||||
for s in stream {
|
||||
let s_live = s?;
|
||||
let bin_output = s_live.as_binary()?;
|
||||
|
||||
if !to_stderr {
|
||||
stdout_write_all_and_flush(bin_output)?
|
||||
} else {
|
||||
stderr_write_all_and_flush(bin_output)?
|
||||
}
|
||||
}
|
||||
return print_if_stream(stream, stderr_stream, to_stderr, exit_code);
|
||||
/*
|
||||
if let Ok(exit_code) = print_if_stream(stream, stderr_stream, to_stderr, exit_code) {
|
||||
return Ok(exit_code);
|
||||
}
|
||||
|
||||
// Make sure everything has finished
|
||||
if let Some(exit_code) = exit_code {
|
||||
let mut exit_codes: Vec<_> = exit_code.into_iter().collect();
|
||||
return match exit_codes.pop() {
|
||||
#[cfg(unix)]
|
||||
Some(Value::Error { error }) => Err(error),
|
||||
Some(Value::Int { val, .. }) => Ok(val),
|
||||
_ => Ok(0),
|
||||
};
|
||||
}
|
||||
|
||||
return Ok(0);
|
||||
*/
|
||||
}
|
||||
|
||||
match engine_state.find_decl("table".as_bytes(), &[]) {
|
||||
|
@ -549,6 +528,42 @@ impl IntoIterator for PipelineData {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn print_if_stream(
|
||||
stream: Option<RawStream>,
|
||||
stderr_stream: Option<RawStream>,
|
||||
to_stderr: bool,
|
||||
exit_code: Option<ListStream>,
|
||||
) -> Result<i64, ShellError> {
|
||||
// NOTE: currently we don't need anything from stderr
|
||||
// so directly consumes `stderr_stream` to make sure that everything is done.
|
||||
std::thread::spawn(move || stderr_stream.map(|x| x.into_bytes()));
|
||||
if let Some(stream) = stream {
|
||||
for s in stream {
|
||||
let s_live = s?;
|
||||
let bin_output = s_live.as_binary()?;
|
||||
|
||||
if !to_stderr {
|
||||
stdout_write_all_and_flush(bin_output)?
|
||||
} else {
|
||||
stderr_write_all_and_flush(bin_output)?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure everything has finished
|
||||
if let Some(exit_code) = exit_code {
|
||||
let mut exit_codes: Vec<_> = exit_code.into_iter().collect();
|
||||
return match exit_codes.pop() {
|
||||
#[cfg(unix)]
|
||||
Some(Value::Error { error }) => Err(error),
|
||||
Some(Value::Int { val, .. }) => Ok(val),
|
||||
_ => Ok(0),
|
||||
};
|
||||
}
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
impl Iterator for PipelineIterator {
|
||||
type Item = Value;
|
||||
|
||||
|
|
|
@ -176,7 +176,7 @@ pub fn nu_repl() {
|
|||
// Check for pre_prompt hook
|
||||
let config = engine_state.get_config();
|
||||
if let Some(hook) = config.hooks.pre_prompt.clone() {
|
||||
if let Err(err) = eval_hook(&mut engine_state, &mut stack, vec![], &hook) {
|
||||
if let Err(err) = eval_hook(&mut engine_state, &mut stack, None, vec![], &hook) {
|
||||
outcome_err(&engine_state, &err);
|
||||
}
|
||||
}
|
||||
|
@ -194,7 +194,7 @@ pub fn nu_repl() {
|
|||
// Check for pre_execution hook
|
||||
let config = engine_state.get_config();
|
||||
if let Some(hook) = config.hooks.pre_execution.clone() {
|
||||
if let Err(err) = eval_hook(&mut engine_state, &mut stack, vec![], &hook) {
|
||||
if let Err(err) = eval_hook(&mut engine_state, &mut stack, None, vec![], &hook) {
|
||||
outcome_err(&engine_state, &err);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue