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,
|
// Right before we start our prompt and take input from the user,
|
||||||
// fire the "pre_prompt" hook
|
// fire the "pre_prompt" hook
|
||||||
if let Some(hook) = config.hooks.pre_prompt.clone() {
|
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);
|
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,
|
// Right before we start running the code the user gave us,
|
||||||
// fire the "pre_execution" hook
|
// fire the "pre_execution" hook
|
||||||
if let Some(hook) = config.hooks.pre_execution.clone() {
|
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);
|
report_error_new(engine_state, &err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -686,6 +686,7 @@ pub fn eval_env_change_hook(
|
||||||
eval_hook(
|
eval_hook(
|
||||||
engine_state,
|
engine_state,
|
||||||
stack,
|
stack,
|
||||||
|
None,
|
||||||
vec![("$before".into(), before), ("$after".into(), after.clone())],
|
vec![("$before".into(), before), ("$after".into(), after.clone())],
|
||||||
hook_value,
|
hook_value,
|
||||||
)?;
|
)?;
|
||||||
|
@ -711,15 +712,17 @@ pub fn eval_env_change_hook(
|
||||||
pub fn eval_hook(
|
pub fn eval_hook(
|
||||||
engine_state: &mut EngineState,
|
engine_state: &mut EngineState,
|
||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
|
input: Option<PipelineData>,
|
||||||
arguments: Vec<(String, Value)>,
|
arguments: Vec<(String, Value)>,
|
||||||
value: &Value,
|
value: &Value,
|
||||||
) -> Result<(), ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let value_span = value.span()?;
|
let value_span = value.span()?;
|
||||||
|
|
||||||
let condition_path = PathMember::String {
|
let condition_path = PathMember::String {
|
||||||
val: "condition".to_string(),
|
val: "condition".to_string(),
|
||||||
span: value_span,
|
span: value_span,
|
||||||
};
|
};
|
||||||
|
let mut output = PipelineData::new(Span::new(0, 0));
|
||||||
|
|
||||||
let code_path = PathMember::String {
|
let code_path = PathMember::String {
|
||||||
val: "code".to_string(),
|
val: "code".to_string(),
|
||||||
|
@ -729,7 +732,7 @@ pub fn eval_hook(
|
||||||
match value {
|
match value {
|
||||||
Value::List { vals, .. } => {
|
Value::List { vals, .. } => {
|
||||||
for val in vals {
|
for val in vals {
|
||||||
eval_hook(engine_state, stack, arguments.clone(), val)?
|
eval_hook(engine_state, stack, None, arguments.clone(), val)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Value::Record { .. } => {
|
Value::Record { .. } => {
|
||||||
|
@ -745,6 +748,7 @@ pub fn eval_hook(
|
||||||
engine_state,
|
engine_state,
|
||||||
stack,
|
stack,
|
||||||
block_id,
|
block_id,
|
||||||
|
None,
|
||||||
arguments.clone(),
|
arguments.clone(),
|
||||||
block_span,
|
block_span,
|
||||||
) {
|
) {
|
||||||
|
@ -824,7 +828,9 @@ pub fn eval_hook(
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
match eval_block(engine_state, stack, &block, input, false, false) {
|
match eval_block(engine_state, stack, &block, input, false, false) {
|
||||||
Ok(_) => {}
|
Ok(pipeline_data) => {
|
||||||
|
output = pipeline_data;
|
||||||
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
report_error_new(engine_state, &err);
|
report_error_new(engine_state, &err);
|
||||||
}
|
}
|
||||||
|
@ -839,7 +845,14 @@ pub fn eval_hook(
|
||||||
span: block_span,
|
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 => {
|
other => {
|
||||||
return Err(ShellError::UnsupportedConfigValue(
|
return Err(ShellError::UnsupportedConfigValue(
|
||||||
|
@ -856,7 +869,17 @@ pub fn eval_hook(
|
||||||
span: block_span,
|
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 => {
|
other => {
|
||||||
return Err(ShellError::UnsupportedConfigValue(
|
return Err(ShellError::UnsupportedConfigValue(
|
||||||
|
@ -870,19 +893,20 @@ pub fn eval_hook(
|
||||||
let cwd = get_guaranteed_cwd(engine_state, stack);
|
let cwd = get_guaranteed_cwd(engine_state, stack);
|
||||||
engine_state.merge_env(stack, cwd)?;
|
engine_state.merge_env(stack, cwd)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(output)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_hook_block(
|
pub fn run_hook_block(
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
block_id: BlockId,
|
block_id: BlockId,
|
||||||
|
optional_input: Option<PipelineData>,
|
||||||
arguments: Vec<(String, Value)>,
|
arguments: Vec<(String, Value)>,
|
||||||
span: Span,
|
span: Span,
|
||||||
) -> Result<Value, ShellError> {
|
) -> Result<Value, ShellError> {
|
||||||
let block = engine_state.get_block(block_id);
|
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);
|
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_engine::eval_block;
|
||||||
use nu_parser::{escape_quote_string, lex, parse, unescape_unquote_string, Token, TokenContents};
|
use nu_parser::{escape_quote_string, lex, parse, unescape_unquote_string, Token, TokenContents};
|
||||||
use nu_protocol::engine::StateWorkingSet;
|
use nu_protocol::engine::StateWorkingSet;
|
||||||
use nu_protocol::CliError;
|
use nu_protocol::CliError;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{EngineState, Stack},
|
engine::{EngineState, Stack},
|
||||||
PipelineData, ShellError, Span, Value,
|
print_if_stream, PipelineData, ShellError, Span, Value,
|
||||||
};
|
};
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use nu_utils::enable_vt_processing;
|
use nu_utils::enable_vt_processing;
|
||||||
|
@ -204,8 +204,6 @@ pub fn eval_source(
|
||||||
fname: &str,
|
fname: &str,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
trace!("eval_source");
|
|
||||||
|
|
||||||
let (block, delta) = {
|
let (block, delta) = {
|
||||||
let mut working_set = StateWorkingSet::new(engine_state);
|
let mut working_set = StateWorkingSet::new(engine_state);
|
||||||
let (output, err) = parse(
|
let (output, err) = parse(
|
||||||
|
@ -232,7 +230,30 @@ pub fn eval_source(
|
||||||
|
|
||||||
match eval_block(engine_state, stack, &block, input, false, false) {
|
match eval_block(engine_state, stack, &block, input, false, false) {
|
||||||
Ok(pipeline_data) => {
|
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) => {
|
Err(err) => {
|
||||||
let working_set = StateWorkingSet::new(engine_state);
|
let working_set = StateWorkingSet::new(engine_state);
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,7 @@ pub struct Hooks {
|
||||||
pub pre_prompt: Option<Value>,
|
pub pre_prompt: Option<Value>,
|
||||||
pub pre_execution: Option<Value>,
|
pub pre_execution: Option<Value>,
|
||||||
pub env_change: Option<Value>,
|
pub env_change: Option<Value>,
|
||||||
|
pub display_output: Option<Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Hooks {
|
impl Hooks {
|
||||||
|
@ -40,6 +41,7 @@ impl Hooks {
|
||||||
pre_prompt: None,
|
pre_prompt: None,
|
||||||
pre_execution: None,
|
pre_execution: None,
|
||||||
env_change: 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_prompt" => hooks.pre_prompt = Some(vals[idx].clone()),
|
||||||
"pre_execution" => hooks.pre_execution = Some(vals[idx].clone()),
|
"pre_execution" => hooks.pre_execution = Some(vals[idx].clone()),
|
||||||
"env_change" => hooks.env_change = Some(vals[idx].clone()),
|
"env_change" => hooks.env_change = Some(vals[idx].clone()),
|
||||||
|
"display_output" => hooks.display_output = Some(vals[idx].clone()),
|
||||||
x => {
|
x => {
|
||||||
return Err(ShellError::UnsupportedConfigValue(
|
return Err(ShellError::UnsupportedConfigValue(
|
||||||
"'pre_prompt', 'pre_execution', or 'env_change'".to_string(),
|
"'pre_prompt', 'pre_execution', 'env_change'".to_string(),
|
||||||
x.to_string(),
|
x.to_string(),
|
||||||
*span,
|
*span,
|
||||||
));
|
));
|
||||||
|
|
|
@ -426,34 +426,13 @@ impl PipelineData {
|
||||||
..
|
..
|
||||||
} = self
|
} = self
|
||||||
{
|
{
|
||||||
// NOTE: currently we don't need anything from stderr
|
return print_if_stream(stream, stderr_stream, to_stderr, exit_code);
|
||||||
// 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 Ok(exit_code) = print_if_stream(stream, stderr_stream, to_stderr, exit_code) {
|
||||||
if let Some(stream) = stream {
|
return Ok(exit_code);
|
||||||
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),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(0);
|
return Ok(0);
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
match engine_state.find_decl("table".as_bytes(), &[]) {
|
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 {
|
impl Iterator for PipelineIterator {
|
||||||
type Item = Value;
|
type Item = Value;
|
||||||
|
|
||||||
|
|
|
@ -176,7 +176,7 @@ pub fn nu_repl() {
|
||||||
// Check for pre_prompt hook
|
// Check for pre_prompt hook
|
||||||
let config = engine_state.get_config();
|
let config = engine_state.get_config();
|
||||||
if let Some(hook) = config.hooks.pre_prompt.clone() {
|
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);
|
outcome_err(&engine_state, &err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -194,7 +194,7 @@ pub fn nu_repl() {
|
||||||
// Check for pre_execution hook
|
// Check for pre_execution hook
|
||||||
let config = engine_state.get_config();
|
let config = engine_state.get_config();
|
||||||
if let Some(hook) = config.hooks.pre_execution.clone() {
|
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);
|
outcome_err(&engine_state, &err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue