forked from extern/nushell
Add initial ctrl-c support
This commit is contained in:
parent
1308eb45d5
commit
bac8b8a450
24
Cargo.lock
generated
24
Cargo.lock
generated
@ -264,6 +264,16 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctrlc"
|
||||
version = "3.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a19c6cedffdc8c03a3346d723eb20bd85a13362bb96dc2ac000842c6381ec7bf"
|
||||
dependencies = [
|
||||
"nix",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dialoguer"
|
||||
version = "0.9.0"
|
||||
@ -339,6 +349,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"assert_cmd",
|
||||
"crossterm",
|
||||
"ctrlc",
|
||||
"dialoguer",
|
||||
"miette",
|
||||
"nu-cli",
|
||||
@ -558,6 +569,19 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f305c2c2e4c39a82f7bf0bf65fb557f9070ce06781d4f2454295cc34b1c43188"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"memoffset",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ntapi"
|
||||
version = "0.3.6"
|
||||
|
@ -22,6 +22,7 @@ nu-protocol = { path = "./crates/nu-protocol" }
|
||||
nu-table = { path = "./crates/nu-table" }
|
||||
nu-term-grid = { path = "./crates/nu-term-grid" }
|
||||
miette = "3.0.0"
|
||||
ctrlc = "3.2.1"
|
||||
# mimalloc = { version = "*", default-features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
|
2
TODO.md
2
TODO.md
@ -32,8 +32,8 @@
|
||||
- [x] Config file loading
|
||||
- [x] block variable captures
|
||||
- [x] improved history and config paths
|
||||
- [x] ctrl-c support
|
||||
- [ ] Support for `$in`
|
||||
- [ ] ctrl-c support
|
||||
- [ ] operator overflow
|
||||
- [ ] shells
|
||||
- [ ] plugins
|
||||
|
@ -26,12 +26,12 @@ impl Command for SubCommand {
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
_engine_state: &EngineState,
|
||||
engine_state: &EngineState,
|
||||
_stack: &mut Stack,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||
into_binary(call, input)
|
||||
into_binary(engine_state, call, input)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -86,27 +86,31 @@ impl Command for SubCommand {
|
||||
}
|
||||
|
||||
fn into_binary(
|
||||
engine_state: &EngineState,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||
let head = call.head;
|
||||
// let column_paths: Vec<CellPath> = call.rest(context, 0)?;
|
||||
|
||||
input.map(move |v| {
|
||||
action(v, head)
|
||||
// FIXME: Add back in cell_path support
|
||||
// if column_paths.is_empty() {
|
||||
// action(v, head)
|
||||
// } else {
|
||||
// let mut ret = v;
|
||||
// for path in &column_paths {
|
||||
// ret =
|
||||
// ret.swap_data_by_cell_path(path, Box::new(move |old| action(old, old.tag())))?;
|
||||
// }
|
||||
input.map(
|
||||
move |v| {
|
||||
action(v, head)
|
||||
// FIXME: Add back in cell_path support
|
||||
// if column_paths.is_empty() {
|
||||
// action(v, head)
|
||||
// } else {
|
||||
// let mut ret = v;
|
||||
// for path in &column_paths {
|
||||
// ret =
|
||||
// ret.swap_data_by_cell_path(path, Box::new(move |old| action(old, old.tag())))?;
|
||||
// }
|
||||
|
||||
// Ok(ret)
|
||||
// }
|
||||
})
|
||||
// Ok(ret)
|
||||
// }
|
||||
},
|
||||
engine_state.ctrlc.clone(),
|
||||
)
|
||||
}
|
||||
|
||||
fn int_to_endian(n: i64) -> Vec<u8> {
|
||||
|
@ -26,12 +26,12 @@ impl Command for SubCommand {
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
_engine_state: &EngineState,
|
||||
engine_state: &EngineState,
|
||||
_stack: &mut Stack,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||
into_filesize(call, input)
|
||||
into_filesize(engine_state, call, input)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -114,30 +114,34 @@ impl Command for SubCommand {
|
||||
}
|
||||
|
||||
fn into_filesize(
|
||||
engine_state: &EngineState,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||
let head = call.head;
|
||||
// let call_paths: Vec<ColumnPath> = args.rest(0)?;
|
||||
|
||||
input.map(move |v| {
|
||||
action(v, head)
|
||||
input.map(
|
||||
move |v| {
|
||||
action(v, head)
|
||||
|
||||
// FIXME: Add back cell_path support
|
||||
// if column_paths.is_empty() {
|
||||
// action(&v, v.tag())
|
||||
// } else {
|
||||
// let mut ret = v;
|
||||
// for path in &column_paths {
|
||||
// ret = ret.swap_data_by_column_path(
|
||||
// path,
|
||||
// Box::new(move |old| action(old, old.tag())),
|
||||
// )?;
|
||||
// }
|
||||
// FIXME: Add back cell_path support
|
||||
// if column_paths.is_empty() {
|
||||
// action(&v, v.tag())
|
||||
// } else {
|
||||
// let mut ret = v;
|
||||
// for path in &column_paths {
|
||||
// ret = ret.swap_data_by_column_path(
|
||||
// path,
|
||||
// Box::new(move |old| action(old, old.tag())),
|
||||
// )?;
|
||||
// }
|
||||
|
||||
// Ok(ret)
|
||||
// }
|
||||
})
|
||||
// Ok(ret)
|
||||
// }
|
||||
},
|
||||
engine_state.ctrlc.clone(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn action(input: Value, span: Span) -> Value {
|
||||
|
@ -26,12 +26,12 @@ impl Command for SubCommand {
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
_engine_state: &EngineState,
|
||||
engine_state: &EngineState,
|
||||
_stack: &mut Stack,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||
into_int(call, input)
|
||||
into_int(engine_state, call, input)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -90,27 +90,31 @@ impl Command for SubCommand {
|
||||
}
|
||||
|
||||
fn into_int(
|
||||
engine_state: &EngineState,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||
let head = call.head;
|
||||
// let column_paths: Vec<CellPath> = call.rest(context, 0)?;
|
||||
|
||||
input.map(move |v| {
|
||||
action(v, head)
|
||||
// FIXME: Add back cell_path support
|
||||
// if column_paths.is_empty() {
|
||||
// action(&v, v.tag())
|
||||
// } else {
|
||||
// let mut ret = v;
|
||||
// for path in &column_paths {
|
||||
// ret = ret
|
||||
// .swap_data_by_column_path(path, Box::new(move |old| action(old, old.tag())))?;
|
||||
// }
|
||||
input.map(
|
||||
move |v| {
|
||||
action(v, head)
|
||||
// FIXME: Add back cell_path support
|
||||
// if column_paths.is_empty() {
|
||||
// action(&v, v.tag())
|
||||
// } else {
|
||||
// let mut ret = v;
|
||||
// for path in &column_paths {
|
||||
// ret = ret
|
||||
// .swap_data_by_column_path(path, Box::new(move |old| action(old, old.tag())))?;
|
||||
// }
|
||||
|
||||
// Ok(ret)
|
||||
// }
|
||||
})
|
||||
// Ok(ret)
|
||||
// }
|
||||
},
|
||||
engine_state.ctrlc.clone(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn action(input: Value, span: Span) -> Value {
|
||||
|
@ -1,7 +1,9 @@
|
||||
use nu_engine::{eval_block, eval_expression};
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{Example, IntoPipelineData, PipelineData, Signature, Span, SyntaxShape, Value};
|
||||
use nu_protocol::{
|
||||
Example, IntoInterruptiblePipelineData, PipelineData, Signature, Span, SyntaxShape, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct For;
|
||||
@ -55,6 +57,7 @@ impl Command for For {
|
||||
.as_block()
|
||||
.expect("internal error: expected block");
|
||||
|
||||
let ctrlc = engine_state.ctrlc.clone();
|
||||
let engine_state = engine_state.clone();
|
||||
let block = engine_state.get_block(block_id).clone();
|
||||
let mut stack = stack.collect_captures(&block.captures);
|
||||
@ -71,7 +74,7 @@ impl Command for For {
|
||||
Err(error) => Value::Error { error },
|
||||
}
|
||||
})
|
||||
.into_pipeline_data()),
|
||||
.into_pipeline_data(ctrlc)),
|
||||
Value::Range { val, .. } => Ok(val
|
||||
.into_range_iter()?
|
||||
.map(move |x| {
|
||||
@ -83,7 +86,7 @@ impl Command for For {
|
||||
Err(error) => Value::Error { error },
|
||||
}
|
||||
})
|
||||
.into_pipeline_data()),
|
||||
.into_pipeline_data(ctrlc)),
|
||||
x => {
|
||||
stack.add_var(var_id, x);
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
use nu_protocol::{
|
||||
ast::Call,
|
||||
engine::{Command, EngineState, Stack},
|
||||
span, Example, IntoPipelineData, PipelineData, ShellError, Signature, Spanned, SyntaxShape,
|
||||
Value,
|
||||
span, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError,
|
||||
Signature, Spanned, SyntaxShape, Value,
|
||||
};
|
||||
|
||||
use nu_engine::{get_full_help, CallExt};
|
||||
@ -121,7 +121,9 @@ fn help(
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(found_cmds_vec.into_iter().into_pipeline_data());
|
||||
return Ok(found_cmds_vec
|
||||
.into_iter()
|
||||
.into_pipeline_data(engine_state.ctrlc.clone()));
|
||||
}
|
||||
|
||||
if !rest.is_empty() {
|
||||
@ -155,7 +157,9 @@ fn help(
|
||||
});
|
||||
}
|
||||
|
||||
Ok(found_cmds_vec.into_iter().into_pipeline_data())
|
||||
Ok(found_cmds_vec
|
||||
.into_iter()
|
||||
.into_pipeline_data(engine_state.ctrlc.clone()))
|
||||
} else {
|
||||
let mut name = String::new();
|
||||
let mut output = String::new();
|
||||
|
@ -7,6 +7,7 @@ use crate::*;
|
||||
|
||||
pub fn create_default_context() -> EngineState {
|
||||
let mut engine_state = EngineState::new();
|
||||
|
||||
let delta = {
|
||||
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||
|
||||
|
@ -7,7 +7,7 @@ use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::Command;
|
||||
use nu_protocol::engine::EngineState;
|
||||
use nu_protocol::engine::Stack;
|
||||
use nu_protocol::IntoPipelineData;
|
||||
use nu_protocol::IntoInterruptiblePipelineData;
|
||||
use nu_protocol::PipelineData;
|
||||
use nu_protocol::{Signature, Value};
|
||||
|
||||
@ -30,7 +30,7 @@ impl Command for ListGitBranches {
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
_engine_state: &EngineState,
|
||||
engine_state: &EngineState,
|
||||
_stack: &mut Stack,
|
||||
call: &Call,
|
||||
_input: PipelineData,
|
||||
@ -62,7 +62,9 @@ impl Command for ListGitBranches {
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(lines.into_iter().into_pipeline_data())
|
||||
Ok(lines
|
||||
.into_iter()
|
||||
.into_pipeline_data(engine_state.ctrlc.clone()))
|
||||
} else {
|
||||
Ok(PipelineData::new())
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ use chrono::{DateTime, Utc};
|
||||
use nu_engine::eval_expression;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{IntoPipelineData, PipelineData, Signature, SyntaxShape, Value};
|
||||
use nu_protocol::{IntoInterruptiblePipelineData, PipelineData, Signature, SyntaxShape, Value};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Ls;
|
||||
@ -112,6 +112,6 @@ impl Command for Ls {
|
||||
},
|
||||
_ => Value::Nothing { span: call_span },
|
||||
})
|
||||
.into_pipeline_data())
|
||||
.into_pipeline_data(engine_state.ctrlc.clone()))
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,9 @@ use std::env::current_dir;
|
||||
use nu_engine::CallExt;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Value};
|
||||
use nu_protocol::{
|
||||
IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, SyntaxShape, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Mkdir;
|
||||
@ -69,6 +71,8 @@ impl Command for Mkdir {
|
||||
}
|
||||
}
|
||||
|
||||
Ok(stream.into_iter().into_pipeline_data())
|
||||
Ok(stream
|
||||
.into_iter()
|
||||
.into_pipeline_data(engine_state.ctrlc.clone()))
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,9 @@ use super::util::get_interactive_confirmation;
|
||||
use nu_engine::CallExt;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Value};
|
||||
use nu_protocol::{
|
||||
IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, SyntaxShape, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Rm;
|
||||
@ -170,7 +172,9 @@ fn rm(
|
||||
// let temp = rm_helper(call, args).flatten();
|
||||
// let temp = input.flatten(call.head, move |_| rm_helper(call, args));
|
||||
|
||||
Ok(response.into_iter().into_pipeline_data())
|
||||
Ok(response
|
||||
.into_iter()
|
||||
.into_pipeline_data(engine_state.ctrlc.clone()))
|
||||
// Ok(Value::Nothing { span })
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,10 @@
|
||||
use nu_engine::eval_block;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{Example, IntoPipelineData, PipelineData, Signature, Span, SyntaxShape, Value};
|
||||
use nu_protocol::{
|
||||
Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, Signature, Span,
|
||||
SyntaxShape, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Each;
|
||||
@ -63,6 +66,7 @@ impl Command for Each {
|
||||
.expect("internal error: expected block");
|
||||
|
||||
let numbered = call.has_flag("numbered");
|
||||
let ctrlc = engine_state.ctrlc.clone();
|
||||
let engine_state = engine_state.clone();
|
||||
let block = engine_state.get_block(block_id).clone();
|
||||
let mut stack = stack.collect_captures(&block.captures);
|
||||
@ -101,7 +105,7 @@ impl Command for Each {
|
||||
Err(error) => Value::Error { error },
|
||||
}
|
||||
})
|
||||
.into_pipeline_data()),
|
||||
.into_pipeline_data(ctrlc)),
|
||||
PipelineData::Value(Value::List { vals: val, .. }) => Ok(val
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
@ -134,7 +138,7 @@ impl Command for Each {
|
||||
Err(error) => Value::Error { error },
|
||||
}
|
||||
})
|
||||
.into_pipeline_data()),
|
||||
.into_pipeline_data(ctrlc)),
|
||||
PipelineData::Stream(stream) => Ok(stream
|
||||
.enumerate()
|
||||
.map(move |(idx, x)| {
|
||||
@ -166,7 +170,7 @@ impl Command for Each {
|
||||
Err(error) => Value::Error { error },
|
||||
}
|
||||
})
|
||||
.into_pipeline_data()),
|
||||
.into_pipeline_data(ctrlc)),
|
||||
PipelineData::Value(Value::Record { cols, vals, .. }) => {
|
||||
let mut output_cols = vec![];
|
||||
let mut output_vals = vec![];
|
||||
|
@ -3,7 +3,8 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape, Value,
|
||||
Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
|
||||
Value,
|
||||
};
|
||||
use std::convert::TryInto;
|
||||
|
||||
@ -54,7 +55,7 @@ impl Command for Last {
|
||||
.into_iter()
|
||||
.skip(beginning_rows_to_skip.try_into().unwrap());
|
||||
|
||||
Ok(iter.into_pipeline_data())
|
||||
Ok(iter.into_pipeline_data(engine_state.ctrlc.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{IntoPipelineData, PipelineData, ShellError, Signature, Value};
|
||||
use nu_protocol::{IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Value};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Lines;
|
||||
@ -22,7 +22,7 @@ impl Command for Lines {
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
_engine_state: &EngineState,
|
||||
engine_state: &EngineState,
|
||||
_stack: &mut Stack,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
@ -46,7 +46,7 @@ impl Command for Lines {
|
||||
}
|
||||
});
|
||||
|
||||
Ok(iter.into_pipeline_data())
|
||||
Ok(iter.into_pipeline_data(engine_state.ctrlc.clone()))
|
||||
}
|
||||
PipelineData::Stream(stream) => {
|
||||
let iter = stream
|
||||
@ -74,7 +74,7 @@ impl Command for Lines {
|
||||
})
|
||||
.flatten();
|
||||
|
||||
Ok(iter.into_pipeline_data())
|
||||
Ok(iter.into_pipeline_data(engine_state.ctrlc.clone()))
|
||||
}
|
||||
PipelineData::Value(val) => Err(ShellError::UnsupportedInput(
|
||||
format!("Not supported input: {}", val.as_string()?),
|
||||
|
@ -1,7 +1,10 @@
|
||||
use nu_engine::eval_block;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{Example, IntoPipelineData, PipelineData, Signature, SyntaxShape, Value};
|
||||
use nu_protocol::{
|
||||
Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, Signature, SyntaxShape,
|
||||
Value,
|
||||
};
|
||||
use rayon::prelude::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -46,6 +49,7 @@ impl Command for ParEach {
|
||||
.expect("internal error: expected block");
|
||||
|
||||
let numbered = call.has_flag("numbered");
|
||||
let ctrlc = engine_state.ctrlc.clone();
|
||||
let engine_state = engine_state.clone();
|
||||
let block = engine_state.get_block(block_id);
|
||||
let mut stack = stack.collect_captures(&block.captures);
|
||||
@ -92,7 +96,7 @@ impl Command for ParEach {
|
||||
.collect::<Vec<_>>()
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.into_pipeline_data()),
|
||||
.into_pipeline_data(ctrlc)),
|
||||
PipelineData::Value(Value::List { vals: val, .. }) => Ok(val
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
@ -133,7 +137,7 @@ impl Command for ParEach {
|
||||
.collect::<Vec<_>>()
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.into_pipeline_data()),
|
||||
.into_pipeline_data(ctrlc)),
|
||||
PipelineData::Stream(stream) => Ok(stream
|
||||
.enumerate()
|
||||
.par_bridge()
|
||||
@ -173,7 +177,7 @@ impl Command for ParEach {
|
||||
.collect::<Vec<_>>()
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.into_pipeline_data()),
|
||||
.into_pipeline_data(ctrlc)),
|
||||
PipelineData::Value(Value::Record { cols, vals, .. }) => {
|
||||
let mut output_cols = vec![];
|
||||
let mut output_vals = vec![];
|
||||
|
@ -2,7 +2,8 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::ast::{Call, CellPath};
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape, Value,
|
||||
Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError, Signature,
|
||||
Span, SyntaxShape, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -35,7 +36,7 @@ impl Command for Select {
|
||||
let columns: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
||||
let span = call.head;
|
||||
|
||||
select(span, columns, input)
|
||||
select(engine_state, span, columns, input)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -55,6 +56,7 @@ impl Command for Select {
|
||||
}
|
||||
|
||||
fn select(
|
||||
engine_state: &EngineState,
|
||||
span: Span,
|
||||
columns: Vec<CellPath>,
|
||||
input: PipelineData,
|
||||
@ -84,7 +86,9 @@ fn select(
|
||||
output.push(Value::Record { cols, vals, span })
|
||||
}
|
||||
|
||||
Ok(output.into_iter().into_pipeline_data())
|
||||
Ok(output
|
||||
.into_iter()
|
||||
.into_pipeline_data(engine_state.ctrlc.clone()))
|
||||
}
|
||||
PipelineData::Stream(stream) => Ok(stream
|
||||
.map(move |x| {
|
||||
@ -106,7 +110,7 @@ fn select(
|
||||
|
||||
Value::Record { cols, vals, span }
|
||||
})
|
||||
.into_pipeline_data()),
|
||||
.into_pipeline_data(engine_state.ctrlc.clone())),
|
||||
PipelineData::Value(v) => {
|
||||
let mut cols = vec![];
|
||||
let mut vals = vec![];
|
||||
|
@ -1,7 +1,10 @@
|
||||
use nu_engine::eval_expression;
|
||||
use nu_protocol::ast::{Call, Expr, Expression};
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Value};
|
||||
use nu_protocol::{
|
||||
IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError, Signature,
|
||||
SyntaxShape, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Where;
|
||||
@ -28,6 +31,7 @@ impl Command for Where {
|
||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||
let cond = call.positional[0].clone();
|
||||
|
||||
let ctrlc = engine_state.ctrlc.clone();
|
||||
let engine_state = engine_state.clone();
|
||||
|
||||
// FIXME: very expensive
|
||||
@ -53,7 +57,7 @@ impl Command for Where {
|
||||
_ => false,
|
||||
}
|
||||
})
|
||||
.into_pipeline_data()),
|
||||
.into_pipeline_data(ctrlc)),
|
||||
PipelineData::Value(Value::List { vals, .. }) => Ok(vals
|
||||
.into_iter()
|
||||
.filter(move |value| {
|
||||
@ -66,7 +70,7 @@ impl Command for Where {
|
||||
_ => false,
|
||||
}
|
||||
})
|
||||
.into_pipeline_data()),
|
||||
.into_pipeline_data(ctrlc)),
|
||||
PipelineData::Value(x) => {
|
||||
stack.add_var(var_id, x.clone());
|
||||
|
||||
|
@ -1,7 +1,9 @@
|
||||
use nu_engine::CallExt;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{IntoPipelineData, PipelineData, Signature, SyntaxShape, Value};
|
||||
use nu_protocol::{
|
||||
IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, Signature, SyntaxShape, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Wrap;
|
||||
@ -37,14 +39,14 @@ impl Command for Wrap {
|
||||
vals: vec![x],
|
||||
span,
|
||||
})
|
||||
.into_pipeline_data()),
|
||||
.into_pipeline_data(engine_state.ctrlc.clone())),
|
||||
PipelineData::Stream(stream) => Ok(stream
|
||||
.map(move |x| Value::Record {
|
||||
cols: vec![name.clone()],
|
||||
vals: vec![x],
|
||||
span,
|
||||
})
|
||||
.into_pipeline_data()),
|
||||
.into_pipeline_data(engine_state.ctrlc.clone())),
|
||||
PipelineData::Value(input) => Ok(Value::Record {
|
||||
cols: vec![name],
|
||||
vals: vec![input],
|
||||
|
@ -1,6 +1,9 @@
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Value};
|
||||
use nu_protocol::{
|
||||
Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError, Signature,
|
||||
Span, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct FromJson;
|
||||
@ -68,7 +71,7 @@ impl Command for FromJson {
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
_engine_state: &EngineState,
|
||||
engine_state: &EngineState,
|
||||
_stack: &mut Stack,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
@ -90,7 +93,7 @@ impl Command for FromJson {
|
||||
Err(error) => Value::Error { error },
|
||||
}
|
||||
})
|
||||
.into_pipeline_data())
|
||||
.into_pipeline_data(engine_state.ctrlc.clone()))
|
||||
} else {
|
||||
Ok(convert_string_to_value(string_input, span)?.into_pipeline_data())
|
||||
}
|
||||
|
@ -20,13 +20,16 @@ impl Command for SubCommand {
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
_engine_state: &EngineState,
|
||||
engine_state: &EngineState,
|
||||
_stack: &mut Stack,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||
let head = call.head;
|
||||
input.map(move |value| abs_helper(value, head))
|
||||
input.map(
|
||||
move |value| abs_helper(value, head),
|
||||
engine_state.ctrlc.clone(),
|
||||
)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
|
@ -24,12 +24,12 @@ impl Command for Size {
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
_engine_state: &EngineState,
|
||||
engine_state: &EngineState,
|
||||
_stack: &mut Stack,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
size(call, input)
|
||||
size(engine_state, call, input)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -100,18 +100,25 @@ impl Command for Size {
|
||||
}
|
||||
}
|
||||
|
||||
fn size(call: &Call, input: PipelineData) -> Result<PipelineData, ShellError> {
|
||||
fn size(
|
||||
engine_state: &EngineState,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let span = call.head;
|
||||
input.map(move |v| match v.as_string() {
|
||||
Ok(s) => count(&s, span),
|
||||
Err(_) => Value::Error {
|
||||
error: ShellError::PipelineMismatch {
|
||||
expected: Type::String,
|
||||
expected_span: span,
|
||||
origin: span,
|
||||
input.map(
|
||||
move |v| match v.as_string() {
|
||||
Ok(s) => count(&s, span),
|
||||
Err(_) => Value::Error {
|
||||
error: ShellError::PipelineMismatch {
|
||||
expected: Type::String,
|
||||
expected_span: span,
|
||||
origin: span,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
engine_state.ctrlc.clone(),
|
||||
)
|
||||
}
|
||||
|
||||
fn count(contents: &str, span: Span) -> Value {
|
||||
|
@ -39,22 +39,26 @@ impl Command for SubCommand {
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
_engine_state: &EngineState,
|
||||
engine_state: &EngineState,
|
||||
_stack: &mut Stack,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||
split_chars(call, input)
|
||||
split_chars(engine_state, call, input)
|
||||
}
|
||||
}
|
||||
|
||||
fn split_chars(
|
||||
engine_state: &EngineState,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||
let span = call.head;
|
||||
|
||||
input.flat_map(move |x| split_chars_helper(&x, span))
|
||||
input.flat_map(
|
||||
move |x| split_chars_helper(&x, span),
|
||||
engine_state.ctrlc.clone(),
|
||||
)
|
||||
}
|
||||
|
||||
fn split_chars_helper(v: &Value, name: Span) -> Vec<Value> {
|
||||
|
@ -54,7 +54,10 @@ fn split_column(
|
||||
let rest: Vec<Spanned<String>> = call.rest(engine_state, stack, 1)?;
|
||||
let collapse_empty = call.has_flag("collapse-empty");
|
||||
|
||||
input.flat_map(move |x| split_column_helper(&x, &separator, &rest, collapse_empty, name_span))
|
||||
input.flat_map(
|
||||
move |x| split_column_helper(&x, &separator, &rest, collapse_empty, name_span),
|
||||
engine_state.ctrlc.clone(),
|
||||
)
|
||||
}
|
||||
|
||||
fn split_column_helper(
|
||||
|
@ -45,7 +45,10 @@ fn split_row(
|
||||
let name_span = call.head;
|
||||
let separator: Spanned<String> = call.req(engine_state, stack, 0)?;
|
||||
|
||||
input.flat_map(move |x| split_row_helper(&x, &separator, name_span))
|
||||
input.flat_map(
|
||||
move |x| split_row_helper(&x, &separator, name_span),
|
||||
engine_state.ctrlc.clone(),
|
||||
)
|
||||
}
|
||||
|
||||
fn split_row_helper(v: &Value, separator: &Spanned<String>, name: Span) -> Vec<Value> {
|
||||
|
@ -1,7 +1,7 @@
|
||||
use nu_protocol::{
|
||||
ast::Call,
|
||||
engine::{Command, EngineState, Stack},
|
||||
Example, IntoPipelineData, PipelineData, ShellError, Signature, Value,
|
||||
Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Value,
|
||||
};
|
||||
use sysinfo::{ProcessExt, System, SystemExt};
|
||||
|
||||
@ -30,12 +30,12 @@ impl Command for Ps {
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
_engine_state: &EngineState,
|
||||
engine_state: &EngineState,
|
||||
_stack: &mut Stack,
|
||||
call: &Call,
|
||||
_input: PipelineData,
|
||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||
run_ps(call)
|
||||
run_ps(engine_state, call)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -47,7 +47,7 @@ impl Command for Ps {
|
||||
}
|
||||
}
|
||||
|
||||
fn run_ps(call: &Call) -> Result<PipelineData, ShellError> {
|
||||
fn run_ps(engine_state: &EngineState, call: &Call) -> Result<PipelineData, ShellError> {
|
||||
let span = call.head;
|
||||
let long = call.has_flag("long");
|
||||
let mut sys = System::new_all();
|
||||
@ -126,5 +126,7 @@ fn run_ps(call: &Call) -> Result<PipelineData, ShellError> {
|
||||
}
|
||||
}
|
||||
|
||||
Ok(output.into_iter().into_pipeline_data())
|
||||
Ok(output
|
||||
.into_iter()
|
||||
.into_pipeline_data(engine_state.ctrlc.clone()))
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ use std::sync::mpsc;
|
||||
|
||||
use nu_protocol::engine::{EngineState, Stack};
|
||||
use nu_protocol::{ast::Call, engine::Command, ShellError, Signature, SyntaxShape, Value};
|
||||
use nu_protocol::{IntoPipelineData, PipelineData, Span, Spanned};
|
||||
use nu_protocol::{IntoInterruptiblePipelineData, PipelineData, Span, Spanned};
|
||||
|
||||
use nu_engine::CallExt;
|
||||
|
||||
@ -49,7 +49,7 @@ impl Command for External {
|
||||
last_expression,
|
||||
env_vars,
|
||||
};
|
||||
command.run_with_input(input)
|
||||
command.run_with_input(engine_state, input)
|
||||
}
|
||||
}
|
||||
|
||||
@ -61,9 +61,15 @@ pub struct ExternalCommand {
|
||||
}
|
||||
|
||||
impl ExternalCommand {
|
||||
pub fn run_with_input(&self, input: PipelineData) -> Result<PipelineData, ShellError> {
|
||||
pub fn run_with_input(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let mut process = self.create_command();
|
||||
|
||||
let ctrlc = engine_state.ctrlc.clone();
|
||||
|
||||
// TODO. We don't have a way to know the current directory
|
||||
// This should be information from the EvaluationContex or EngineState
|
||||
let path = env::current_dir().unwrap();
|
||||
@ -155,7 +161,7 @@ impl ExternalCommand {
|
||||
});
|
||||
|
||||
// The ValueStream is consumed by the next expression in the pipeline
|
||||
ChannelReceiver::new(rx).into_pipeline_data()
|
||||
ChannelReceiver::new(rx).into_pipeline_data(ctrlc)
|
||||
} else {
|
||||
PipelineData::new()
|
||||
};
|
||||
|
@ -1,7 +1,10 @@
|
||||
use super::Command;
|
||||
use crate::{ast::Block, BlockId, DeclId, Example, Signature, Span, Type, VarId};
|
||||
use core::panic;
|
||||
use std::collections::HashMap;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
sync::{atomic::AtomicBool, Arc},
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct EngineState {
|
||||
@ -11,6 +14,7 @@ pub struct EngineState {
|
||||
decls: im::Vector<Box<dyn Command + 'static>>,
|
||||
blocks: im::Vector<Block>,
|
||||
pub scope: im::Vector<ScopeFrame>,
|
||||
pub ctrlc: Option<Arc<AtomicBool>>,
|
||||
}
|
||||
|
||||
// Tells whether a decl etc. is visible or not
|
||||
@ -102,6 +106,7 @@ impl EngineState {
|
||||
decls: im::vector![],
|
||||
blocks: im::vector![],
|
||||
scope: im::vector![ScopeFrame::new()],
|
||||
ctrlc: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
use std::sync::{atomic::AtomicBool, Arc};
|
||||
|
||||
use crate::{ast::PathMember, ShellError, Span, Value, ValueStream};
|
||||
|
||||
pub enum PipelineData {
|
||||
@ -40,18 +42,22 @@ impl PipelineData {
|
||||
}
|
||||
|
||||
/// Simplified mapper to help with simple values also. For full iterator support use `.into_iter()` instead
|
||||
pub fn map<F>(self, mut f: F) -> Result<PipelineData, ShellError>
|
||||
pub fn map<F>(
|
||||
self,
|
||||
mut f: F,
|
||||
ctrlc: Option<Arc<AtomicBool>>,
|
||||
) -> Result<PipelineData, ShellError>
|
||||
where
|
||||
Self: Sized,
|
||||
F: FnMut(Value) -> Value + 'static + Send,
|
||||
{
|
||||
match self {
|
||||
PipelineData::Value(Value::List { vals, .. }) => {
|
||||
Ok(vals.into_iter().map(f).into_pipeline_data())
|
||||
Ok(vals.into_iter().map(f).into_pipeline_data(ctrlc))
|
||||
}
|
||||
PipelineData::Stream(stream) => Ok(stream.map(f).into_pipeline_data()),
|
||||
PipelineData::Stream(stream) => Ok(stream.map(f).into_pipeline_data(ctrlc)),
|
||||
PipelineData::Value(Value::Range { val, .. }) => {
|
||||
Ok(val.into_range_iter()?.map(f).into_pipeline_data())
|
||||
Ok(val.into_range_iter()?.map(f).into_pipeline_data(ctrlc))
|
||||
}
|
||||
PipelineData::Value(v) => {
|
||||
let output = f(v);
|
||||
@ -64,7 +70,11 @@ impl PipelineData {
|
||||
}
|
||||
|
||||
/// Simplified flatmapper. For full iterator support use `.into_iter()` instead
|
||||
pub fn flat_map<U, F>(self, mut f: F) -> Result<PipelineData, ShellError>
|
||||
pub fn flat_map<U, F>(
|
||||
self,
|
||||
mut f: F,
|
||||
ctrlc: Option<Arc<AtomicBool>>,
|
||||
) -> Result<PipelineData, ShellError>
|
||||
where
|
||||
Self: Sized,
|
||||
U: IntoIterator<Item = Value>,
|
||||
@ -73,14 +83,14 @@ impl PipelineData {
|
||||
{
|
||||
match self {
|
||||
PipelineData::Value(Value::List { vals, .. }) => {
|
||||
Ok(vals.into_iter().map(f).flatten().into_pipeline_data())
|
||||
Ok(vals.into_iter().map(f).flatten().into_pipeline_data(ctrlc))
|
||||
}
|
||||
PipelineData::Stream(stream) => Ok(stream.map(f).flatten().into_pipeline_data()),
|
||||
PipelineData::Stream(stream) => Ok(stream.map(f).flatten().into_pipeline_data(ctrlc)),
|
||||
PipelineData::Value(Value::Range { val, .. }) => match val.into_range_iter() {
|
||||
Ok(iter) => Ok(iter.map(f).flatten().into_pipeline_data()),
|
||||
Ok(iter) => Ok(iter.map(f).flatten().into_pipeline_data(ctrlc)),
|
||||
Err(error) => Err(error),
|
||||
},
|
||||
PipelineData::Value(v) => Ok(f(v).into_iter().into_pipeline_data()),
|
||||
PipelineData::Value(v) => Ok(f(v).into_iter().into_pipeline_data(ctrlc)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -100,9 +110,12 @@ impl IntoIterator for PipelineData {
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
match self {
|
||||
PipelineData::Value(Value::List { vals, .. }) => PipelineIterator(
|
||||
PipelineData::Stream(ValueStream(Box::new(vals.into_iter()))),
|
||||
),
|
||||
PipelineData::Value(Value::List { vals, .. }) => {
|
||||
PipelineIterator(PipelineData::Stream(ValueStream {
|
||||
stream: Box::new(vals.into_iter()),
|
||||
ctrlc: None,
|
||||
}))
|
||||
}
|
||||
x => PipelineIterator(x),
|
||||
}
|
||||
}
|
||||
@ -133,11 +146,18 @@ impl IntoPipelineData for Value {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IntoPipelineData for T
|
||||
pub trait IntoInterruptiblePipelineData {
|
||||
fn into_pipeline_data(self, ctrlc: Option<Arc<AtomicBool>>) -> PipelineData;
|
||||
}
|
||||
|
||||
impl<T> IntoInterruptiblePipelineData for T
|
||||
where
|
||||
T: Iterator<Item = Value> + Send + 'static,
|
||||
{
|
||||
fn into_pipeline_data(self) -> PipelineData {
|
||||
PipelineData::Stream(ValueStream(Box::new(self)))
|
||||
fn into_pipeline_data(self, ctrlc: Option<Arc<AtomicBool>>) -> PipelineData {
|
||||
PipelineData::Stream(ValueStream {
|
||||
stream: Box::new(self),
|
||||
ctrlc,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,16 @@
|
||||
use crate::*;
|
||||
use std::fmt::Debug;
|
||||
use std::{
|
||||
fmt::Debug,
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
},
|
||||
};
|
||||
|
||||
pub struct ValueStream(pub Box<dyn Iterator<Item = Value> + Send + 'static>);
|
||||
pub struct ValueStream {
|
||||
pub stream: Box<dyn Iterator<Item = Value> + Send + 'static>,
|
||||
pub ctrlc: Option<Arc<AtomicBool>>,
|
||||
}
|
||||
|
||||
impl ValueStream {
|
||||
pub fn into_string(self) -> String {
|
||||
@ -19,8 +28,14 @@ impl ValueStream {
|
||||
.join("\n")
|
||||
}
|
||||
|
||||
pub fn from_stream(input: impl Iterator<Item = Value> + Send + 'static) -> ValueStream {
|
||||
ValueStream(Box::new(input))
|
||||
pub fn from_stream(
|
||||
input: impl Iterator<Item = Value> + Send + 'static,
|
||||
ctrlc: Option<Arc<AtomicBool>>,
|
||||
) -> ValueStream {
|
||||
ValueStream {
|
||||
stream: Box::new(input),
|
||||
ctrlc,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,8 +49,14 @@ impl Iterator for ValueStream {
|
||||
type Item = Value;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
{
|
||||
self.0.next()
|
||||
if let Some(ctrlc) = &self.ctrlc {
|
||||
if ctrlc.load(Ordering::SeqCst) {
|
||||
None
|
||||
} else {
|
||||
self.stream.next()
|
||||
}
|
||||
} else {
|
||||
self.stream.next()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
25
src/main.rs
25
src/main.rs
@ -1,4 +1,10 @@
|
||||
use std::io::Write;
|
||||
use std::{
|
||||
io::Write,
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
},
|
||||
};
|
||||
|
||||
use dialoguer::{
|
||||
console::{Style, Term},
|
||||
@ -84,6 +90,20 @@ fn main() -> Result<()> {
|
||||
|
||||
let mut engine_state = create_default_context();
|
||||
|
||||
// TODO: make this conditional in the future
|
||||
// Ctrl-c protection section
|
||||
let ctrlc = Arc::new(AtomicBool::new(false));
|
||||
let handler_ctrlc = ctrlc.clone();
|
||||
let engine_state_ctrlc = ctrlc.clone();
|
||||
|
||||
ctrlc::set_handler(move || {
|
||||
handler_ctrlc.store(true, Ordering::SeqCst);
|
||||
})
|
||||
.expect("Error setting Ctrl-C handler");
|
||||
|
||||
engine_state.ctrlc = Some(engine_state_ctrlc);
|
||||
// End ctrl-c protection section
|
||||
|
||||
if let Some(path) = std::env::args().nth(1) {
|
||||
let file = std::fs::read(&path).into_diagnostic()?;
|
||||
|
||||
@ -153,6 +173,9 @@ fn main() -> Result<()> {
|
||||
};
|
||||
|
||||
loop {
|
||||
//Reset the ctrl-c handler
|
||||
ctrlc.store(false, Ordering::SeqCst);
|
||||
|
||||
let line_editor = Reedline::create()
|
||||
.into_diagnostic()?
|
||||
.with_completion_action_handler(Box::new(FuzzyCompletion {
|
||||
|
Loading…
Reference in New Issue
Block a user