mirror of
https://github.com/nushell/nushell.git
synced 2025-04-14 00:08:20 +02:00
# Description Part of the larger cratification effort. Moves all `reedline` or shell line editor specific commands to `nu-cli`. ## From `nu-cmd-lang`: - `commandline` - This shouldn't have moved there. Doesn't directly depend on reedline but assumes parts in the engine state that are specific to the use of reedline or a REPL ## From `nu-command`: - `keybindings` and subcommands - `keybindings default` - `keybindings list` - `keybindings listen` - very `reedline` specific - `history` - needs `reedline` - `history session` ## internal use Instead of having a separate `create_default_context()` that calls `nu-command`'s `create_default_context()`, I added a `add_cli_context()` that updates an `EngineState` # User-Facing Changes None ## Build time comparison `cargo build --timings` from a `cargo clean --profile dev` ### total main: 64 secs this: 59 secs ### `nu-command` build time branch | total| codegen | fraction ---|---|---|--- main | 14.0s | 6.2s | (44%) this | 12.5s | 5.5s | (44%) `nu-cli` depends on `nu-command` at the moment. Thus it is built during the code-gen phase of `nu-command` (on 16 virtual cores) # Tests + Formatting I removed the `test_example()` facilities for now as we had not run any of the commands in an `Example` test and importing the right context for those tests seemed more of a hassle than the duplicated `test_examples()` implementations in `nu-cmd-lang` and `nu-command`
138 lines
4.6 KiB
Rust
138 lines
4.6 KiB
Rust
use nu_engine::CallExt;
|
|
use nu_protocol::ast::Call;
|
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
|
use nu_protocol::Category;
|
|
use nu_protocol::IntoPipelineData;
|
|
use nu_protocol::{PipelineData, ShellError, Signature, SyntaxShape, Type, Value};
|
|
use unicode_segmentation::UnicodeSegmentation;
|
|
|
|
#[derive(Clone)]
|
|
pub struct Commandline;
|
|
|
|
impl Command for Commandline {
|
|
fn name(&self) -> &str {
|
|
"commandline"
|
|
}
|
|
|
|
fn signature(&self) -> Signature {
|
|
Signature::build("commandline")
|
|
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
|
|
.switch(
|
|
"cursor",
|
|
"Set or get the current cursor position",
|
|
Some('c'),
|
|
)
|
|
.switch(
|
|
"append",
|
|
"appends the string to the end of the buffer",
|
|
Some('a'),
|
|
)
|
|
.switch(
|
|
"insert",
|
|
"inserts the string into the buffer at the cursor position",
|
|
Some('i'),
|
|
)
|
|
.switch(
|
|
"replace",
|
|
"replaces the current contents of the buffer (default)",
|
|
Some('r'),
|
|
)
|
|
.optional(
|
|
"cmd",
|
|
SyntaxShape::String,
|
|
"the string to perform the operation with",
|
|
)
|
|
.category(Category::Core)
|
|
}
|
|
|
|
fn usage(&self) -> &str {
|
|
"View or modify the current command line input buffer."
|
|
}
|
|
|
|
fn search_terms(&self) -> Vec<&str> {
|
|
vec!["repl", "interactive"]
|
|
}
|
|
|
|
fn run(
|
|
&self,
|
|
engine_state: &EngineState,
|
|
stack: &mut Stack,
|
|
call: &Call,
|
|
_input: PipelineData,
|
|
) -> Result<PipelineData, ShellError> {
|
|
if let Some(cmd) = call.opt::<Value>(engine_state, stack, 0)? {
|
|
let mut buffer = engine_state
|
|
.repl_buffer_state
|
|
.lock()
|
|
.expect("repl buffer state mutex");
|
|
let mut cursor_pos = engine_state
|
|
.repl_cursor_pos
|
|
.lock()
|
|
.expect("repl cursor pos mutex");
|
|
|
|
if call.has_flag("cursor") {
|
|
let cmd_str = cmd.as_string()?;
|
|
match cmd_str.parse::<i64>() {
|
|
Ok(n) => {
|
|
*cursor_pos = if n <= 0 {
|
|
0usize
|
|
} else {
|
|
buffer
|
|
.grapheme_indices(true)
|
|
.map(|(i, _c)| i)
|
|
.nth(n as usize)
|
|
.unwrap_or(buffer.len())
|
|
}
|
|
}
|
|
Err(_) => {
|
|
return Err(ShellError::CantConvert {
|
|
to_type: "int".to_string(),
|
|
from_type: "string".to_string(),
|
|
span: cmd.span()?,
|
|
help: Some(format!(
|
|
r#"string "{cmd_str}" does not represent a valid integer"#
|
|
)),
|
|
})
|
|
}
|
|
}
|
|
} else if call.has_flag("append") {
|
|
buffer.push_str(&cmd.as_string()?);
|
|
} else if call.has_flag("insert") {
|
|
let cmd_str = cmd.as_string()?;
|
|
buffer.insert_str(*cursor_pos, &cmd_str);
|
|
*cursor_pos += cmd_str.len();
|
|
} else {
|
|
*buffer = cmd.as_string()?;
|
|
*cursor_pos = buffer.len();
|
|
}
|
|
Ok(Value::Nothing { span: call.head }.into_pipeline_data())
|
|
} else {
|
|
let buffer = engine_state
|
|
.repl_buffer_state
|
|
.lock()
|
|
.expect("repl buffer state mutex");
|
|
if call.has_flag("cursor") {
|
|
let cursor_pos = engine_state
|
|
.repl_cursor_pos
|
|
.lock()
|
|
.expect("repl cursor pos mutex");
|
|
let char_pos = buffer
|
|
.grapheme_indices(true)
|
|
.position(|(i, _c)| i == *cursor_pos)
|
|
.unwrap_or(buffer.len());
|
|
Ok(Value::String {
|
|
val: char_pos.to_string(),
|
|
span: call.head,
|
|
}
|
|
.into_pipeline_data())
|
|
} else {
|
|
Ok(Value::String {
|
|
val: buffer.to_string(),
|
|
span: call.head,
|
|
}
|
|
.into_pipeline_data())
|
|
}
|
|
}
|
|
}
|
|
}
|