mirror of
https://github.com/sigoden/aichat
synced 2024-11-16 06:15:26 +00:00
feat: add .edit command for multi-line editing (#151)
This commit is contained in:
parent
b3f8f27018
commit
ca0f6343f2
@ -32,15 +32,12 @@ impl Repl {
|
||||
let edit_mode: Box<dyn EditMode> = if config.read().vi_keybindings {
|
||||
let mut normal_keybindings = default_vi_normal_keybindings();
|
||||
let mut insert_keybindings = default_vi_insert_keybindings();
|
||||
Self::add_menu_keybindings(&mut normal_keybindings);
|
||||
Self::add_menu_keybindings(&mut insert_keybindings);
|
||||
Self::add_clear_keybindings(&mut normal_keybindings);
|
||||
Self::add_clear_keybindings(&mut insert_keybindings);
|
||||
Self::extra_keybindings(&mut normal_keybindings);
|
||||
Self::extra_keybindings(&mut insert_keybindings);
|
||||
Box::new(Vi::new(insert_keybindings, normal_keybindings))
|
||||
} else {
|
||||
let mut keybindings = default_emacs_keybindings();
|
||||
Self::add_menu_keybindings(&mut keybindings);
|
||||
Self::add_clear_keybindings(&mut keybindings);
|
||||
Self::extra_keybindings(&mut keybindings);
|
||||
Box::new(Emacs::new(keybindings))
|
||||
};
|
||||
let mut editor = Reedline::create()
|
||||
@ -67,7 +64,7 @@ impl Repl {
|
||||
completer
|
||||
}
|
||||
|
||||
fn add_menu_keybindings(keybindings: &mut Keybindings) {
|
||||
fn extra_keybindings(keybindings: &mut Keybindings) {
|
||||
keybindings.add_binding(
|
||||
KeyModifiers::NONE,
|
||||
KeyCode::Tab,
|
||||
@ -76,14 +73,16 @@ impl Repl {
|
||||
ReedlineEvent::MenuNext,
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
fn add_clear_keybindings(keybindings: &mut Keybindings) {
|
||||
keybindings.add_binding(
|
||||
KeyModifiers::CONTROL,
|
||||
KeyCode::Char('l'),
|
||||
ReedlineEvent::ExecuteHostCommand(".clear screen".into()),
|
||||
);
|
||||
keybindings.add_binding(
|
||||
KeyModifiers::CONTROL,
|
||||
KeyCode::Char('s'),
|
||||
ReedlineEvent::Submit,
|
||||
);
|
||||
}
|
||||
|
||||
fn create_menu() -> ReedlineMenu {
|
||||
|
@ -14,10 +14,12 @@ use crate::print_now;
|
||||
use crate::term;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use fancy_regex::Regex;
|
||||
use lazy_static::lazy_static;
|
||||
use reedline::Signal;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub const REPL_COMMANDS: [(&str, &str); 14] = [
|
||||
pub const REPL_COMMANDS: [(&str, &str); 15] = [
|
||||
(".info", "Print system-wide information"),
|
||||
(".set", "Modify the configuration temporarily"),
|
||||
(".model", "Choose a model"),
|
||||
@ -28,12 +30,17 @@ pub const REPL_COMMANDS: [(&str, &str); 14] = [
|
||||
(".clear conversation", "End current conversation."),
|
||||
(".copy", "Copy the last output to the clipboard"),
|
||||
(".read", "Read the contents of a file into the prompt"),
|
||||
(".edit", "Multi-line editing (CTRL+S to finish)"),
|
||||
(".history", "Print the history"),
|
||||
(".clear history", "Clear the history"),
|
||||
(".help", "Print this help message"),
|
||||
(".exit", "Exit the REPL"),
|
||||
];
|
||||
|
||||
lazy_static! {
|
||||
static ref EDIT_RE: Regex = Regex::new(r"^\s*.edit\s*").unwrap();
|
||||
}
|
||||
|
||||
impl Repl {
|
||||
pub fn run(&mut self, config: SharedConfig) -> Result<()> {
|
||||
let abort = AbortSignal::new();
|
||||
@ -85,8 +92,7 @@ impl Repl {
|
||||
}
|
||||
|
||||
fn handle_line(&mut self, handler: &Rc<ReplCmdHandler>, line: &str) -> Result<bool> {
|
||||
let line = line.trim().replace("\\\n", "\n");
|
||||
match parse_command(line.as_ref()) {
|
||||
match parse_command(line) {
|
||||
Some((cmd, args)) => match cmd {
|
||||
".exit" => {
|
||||
return Ok(true);
|
||||
@ -119,10 +125,6 @@ impl Repl {
|
||||
Some(name) => handler.handle(ReplCmd::SetRole(name.to_string()))?,
|
||||
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" => {
|
||||
handler.handle(ReplCmd::ViewInfo)?;
|
||||
}
|
||||
@ -144,6 +146,15 @@ impl Repl {
|
||||
".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(),
|
||||
},
|
||||
None => {
|
||||
|
@ -1,12 +1,13 @@
|
||||
use super::EDIT_RE;
|
||||
|
||||
use reedline::{ValidationResult, Validator};
|
||||
|
||||
/// A default validator which checks for mismatched quotes and brackets
|
||||
#[allow(clippy::module_name_repetitions)]
|
||||
pub struct ReplValidator;
|
||||
|
||||
impl Validator for ReplValidator {
|
||||
fn validate(&self, line: &str) -> ValidationResult {
|
||||
if line.ends_with('\\') {
|
||||
if let Ok(true) = EDIT_RE.is_match(line) {
|
||||
ValidationResult::Incomplete
|
||||
} else {
|
||||
ValidationResult::Complete
|
||||
|
Loading…
Reference in New Issue
Block a user