feat: add .edit command for multi-line editing (#151)

pull/153/head
sigoden 8 months ago committed by GitHub
parent b3f8f27018
commit ca0f6343f2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -32,15 +32,12 @@ impl Repl {
let edit_mode: Box<dyn EditMode> = if config.read().vi_keybindings { let edit_mode: Box<dyn EditMode> = if config.read().vi_keybindings {
let mut normal_keybindings = default_vi_normal_keybindings(); let mut normal_keybindings = default_vi_normal_keybindings();
let mut insert_keybindings = default_vi_insert_keybindings(); let mut insert_keybindings = default_vi_insert_keybindings();
Self::add_menu_keybindings(&mut normal_keybindings); Self::extra_keybindings(&mut normal_keybindings);
Self::add_menu_keybindings(&mut insert_keybindings); Self::extra_keybindings(&mut insert_keybindings);
Self::add_clear_keybindings(&mut normal_keybindings);
Self::add_clear_keybindings(&mut insert_keybindings);
Box::new(Vi::new(insert_keybindings, normal_keybindings)) Box::new(Vi::new(insert_keybindings, normal_keybindings))
} else { } else {
let mut keybindings = default_emacs_keybindings(); let mut keybindings = default_emacs_keybindings();
Self::add_menu_keybindings(&mut keybindings); Self::extra_keybindings(&mut keybindings);
Self::add_clear_keybindings(&mut keybindings);
Box::new(Emacs::new(keybindings)) Box::new(Emacs::new(keybindings))
}; };
let mut editor = Reedline::create() let mut editor = Reedline::create()
@ -67,7 +64,7 @@ impl Repl {
completer completer
} }
fn add_menu_keybindings(keybindings: &mut Keybindings) { fn extra_keybindings(keybindings: &mut Keybindings) {
keybindings.add_binding( keybindings.add_binding(
KeyModifiers::NONE, KeyModifiers::NONE,
KeyCode::Tab, KeyCode::Tab,
@ -76,14 +73,16 @@ impl Repl {
ReedlineEvent::MenuNext, ReedlineEvent::MenuNext,
]), ]),
); );
}
fn add_clear_keybindings(keybindings: &mut Keybindings) {
keybindings.add_binding( keybindings.add_binding(
KeyModifiers::CONTROL, KeyModifiers::CONTROL,
KeyCode::Char('l'), KeyCode::Char('l'),
ReedlineEvent::ExecuteHostCommand(".clear screen".into()), ReedlineEvent::ExecuteHostCommand(".clear screen".into()),
); );
keybindings.add_binding(
KeyModifiers::CONTROL,
KeyCode::Char('s'),
ReedlineEvent::Submit,
);
} }
fn create_menu() -> ReedlineMenu { fn create_menu() -> ReedlineMenu {

@ -14,10 +14,12 @@ use crate::print_now;
use crate::term; use crate::term;
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use fancy_regex::Regex;
use lazy_static::lazy_static;
use reedline::Signal; use reedline::Signal;
use std::rc::Rc; use std::rc::Rc;
pub const REPL_COMMANDS: [(&str, &str); 14] = [ pub const REPL_COMMANDS: [(&str, &str); 15] = [
(".info", "Print system-wide information"), (".info", "Print system-wide information"),
(".set", "Modify the configuration temporarily"), (".set", "Modify the configuration temporarily"),
(".model", "Choose a model"), (".model", "Choose a model"),
@ -28,12 +30,17 @@ pub const REPL_COMMANDS: [(&str, &str); 14] = [
(".clear conversation", "End current conversation."), (".clear conversation", "End current conversation."),
(".copy", "Copy the last output to the clipboard"), (".copy", "Copy the last output to the clipboard"),
(".read", "Read the contents of a file into the prompt"), (".read", "Read the contents of a file into the prompt"),
(".edit", "Multi-line editing (CTRL+S to finish)"),
(".history", "Print the history"), (".history", "Print the history"),
(".clear history", "Clear the history"), (".clear history", "Clear the history"),
(".help", "Print this help message"), (".help", "Print this help message"),
(".exit", "Exit the REPL"), (".exit", "Exit the REPL"),
]; ];
lazy_static! {
static ref EDIT_RE: Regex = Regex::new(r"^\s*.edit\s*").unwrap();
}
impl Repl { impl Repl {
pub fn run(&mut self, config: SharedConfig) -> Result<()> { pub fn run(&mut self, config: SharedConfig) -> Result<()> {
let abort = AbortSignal::new(); let abort = AbortSignal::new();
@ -85,8 +92,7 @@ impl Repl {
} }
fn handle_line(&mut self, handler: &Rc<ReplCmdHandler>, line: &str) -> Result<bool> { fn handle_line(&mut self, handler: &Rc<ReplCmdHandler>, line: &str) -> Result<bool> {
let line = line.trim().replace("\\\n", "\n"); match parse_command(line) {
match parse_command(line.as_ref()) {
Some((cmd, args)) => match cmd { Some((cmd, args)) => match cmd {
".exit" => { ".exit" => {
return Ok(true); return Ok(true);
@ -119,10 +125,6 @@ impl Repl {
Some(name) => handler.handle(ReplCmd::SetRole(name.to_string()))?, Some(name) => handler.handle(ReplCmd::SetRole(name.to_string()))?,
None => print_now!("Usage: .role <name>\n\n"), None => print_now!("Usage: .role <name>\n\n"),
}, },
".read" => match args {
Some(file) => handler.handle(ReplCmd::ReadFile(file.to_string()))?,
None => print_now!("Usage: .read <file name>\n\n"),
},
".info" => { ".info" => {
handler.handle(ReplCmd::ViewInfo)?; handler.handle(ReplCmd::ViewInfo)?;
} }
@ -144,6 +146,15 @@ impl Repl {
".copy" => { ".copy" => {
handler.handle(ReplCmd::Copy)?; handler.handle(ReplCmd::Copy)?;
} }
".read" => match args {
Some(file) => handler.handle(ReplCmd::ReadFile(file.to_string()))?,
None => print_now!("Usage: .read <file name>\n\n"),
},
".edit" => {
if let Some(text) = args {
handler.handle(ReplCmd::Submit(text.to_string()))?;
}
}
_ => dump_unknown_command(), _ => dump_unknown_command(),
}, },
None => { None => {

@ -1,12 +1,13 @@
use super::EDIT_RE;
use reedline::{ValidationResult, Validator}; use reedline::{ValidationResult, Validator};
/// A default validator which checks for mismatched quotes and brackets /// A default validator which checks for mismatched quotes and brackets
#[allow(clippy::module_name_repetitions)]
pub struct ReplValidator; pub struct ReplValidator;
impl Validator for ReplValidator { impl Validator for ReplValidator {
fn validate(&self, line: &str) -> ValidationResult { fn validate(&self, line: &str) -> ValidationResult {
if line.ends_with('\\') { if let Ok(true) = EDIT_RE.is_match(line) {
ValidationResult::Incomplete ValidationResult::Incomplete
} else { } else {
ValidationResult::Complete ValidationResult::Complete

Loading…
Cancel
Save