summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/config/role.rs2
-rw-r--r--src/repl/init.rs49
-rw-r--r--src/repl/mod.rs115
-rw-r--r--src/repl/prompt.rs4
4 files changed, 81 insertions, 89 deletions
diff --git a/src/config/role.rs b/src/config/role.rs
index 5155ea1..16f7bc1 100644
--- a/src/config/role.rs
+++ b/src/config/role.rs
@@ -39,7 +39,7 @@ impl Role {
if self.embeded() {
merge_prompt_content(&self.prompt, content)
} else {
- format!("{}{content}", self.prompt)
+ format!("{}\n{content}", self.prompt)
}
}
diff --git a/src/repl/init.rs b/src/repl/init.rs
index 61351fc..7d0c2bb 100644
--- a/src/repl/init.rs
+++ b/src/repl/init.rs
@@ -4,8 +4,8 @@ use crate::config::{Config, SharedConfig};
use anyhow::{Context, Result};
use reedline::{
- default_emacs_keybindings, ColumnarMenu, DefaultCompleter, Emacs, FileBackedHistory, KeyCode,
- KeyModifiers, Keybindings, Reedline, ReedlineEvent, ReedlineMenu, ValidationResult, Validator,
+ default_emacs_keybindings, ColumnarMenu, DefaultCompleter, DefaultValidator, Emacs,
+ FileBackedHistory, KeyCode, KeyModifiers, Keybindings, Reedline, ReedlineEvent, ReedlineMenu,
};
const MENU_NAME: &str = "completion_menu";
@@ -16,11 +16,6 @@ pub struct Repl {
impl Repl {
pub fn init(config: SharedConfig) -> Result<Self> {
- let multiline_commands: Vec<&'static str> = REPL_COMMANDS
- .iter()
- .filter(|(_, _, v)| *v)
- .map(|(v, _, _)| *v)
- .collect();
let completer = Self::create_completer(config);
let keybindings = Self::create_keybindings();
let history = Self::create_history()?;
@@ -33,7 +28,7 @@ impl Repl {
.with_edit_mode(edit_mode)
.with_quick_completions(true)
.with_partial_completions(true)
- .with_validator(Box::new(ReplValidator { multiline_commands }))
+ .with_validator(Box::new(DefaultValidator))
.with_ansi_colors(true);
Ok(Self { editor })
}
@@ -41,7 +36,7 @@ impl Repl {
fn create_completer(config: SharedConfig) -> DefaultCompleter {
let mut completion: Vec<String> = REPL_COMMANDS
.into_iter()
- .map(|(v, _, _)| v.to_string())
+ .map(|(v, _)| v.to_string())
.collect();
completion.extend(config.lock().repl_completions());
let mut completer = DefaultCompleter::with_inclusions(&['.', '-', '_']).set_min_word_len(2);
@@ -79,39 +74,3 @@ impl Repl {
))
}
}
-
-struct ReplValidator {
- multiline_commands: Vec<&'static str>,
-}
-
-impl Validator for ReplValidator {
- fn validate(&self, line: &str) -> ValidationResult {
- if line.split('"').count() % 2 == 0 || incomplete_brackets(line, &self.multiline_commands) {
- ValidationResult::Incomplete
- } else {
- ValidationResult::Complete
- }
- }
-}
-
-fn incomplete_brackets(line: &str, multiline_commands: &[&str]) -> bool {
- let mut balance: Vec<char> = Vec::new();
- let line = line.trim_start();
- if !multiline_commands.iter().any(|v| line.starts_with(v)) {
- return false;
- }
-
- for c in line.chars() {
- if c == '{' {
- balance.push('}');
- } else if c == '}' {
- if let Some(last) = balance.last() {
- if last == &c {
- balance.pop();
- }
- }
- }
- }
-
- !balance.is_empty()
-}
diff --git a/src/repl/mod.rs b/src/repl/mod.rs
index 9d7d1cf..b9a6328 100644
--- a/src/repl/mod.rs
+++ b/src/repl/mod.rs
@@ -15,21 +15,21 @@ use crate::term;
use anyhow::{Context, Result};
use reedline::Signal;
+use std::borrow::Cow;
use std::sync::Arc;
-pub const REPL_COMMANDS: [(&str, &str, bool); 12] = [
- (".info", "Print the information", false),
- (".set", "Modify the configuration temporarily", false),
- (".prompt", "Add a GPT prompt", true),
- (".role", "Select a role", false),
- (".clear role", "Clear the currently selected role", false),
- (".conversation", "Start a conversation.", false),
- (".clear conversation", "End current conversation.", false),
- (".history", "Print the history", false),
- (".clear history", "Clear the history", false),
- (".editor", "Enter editor mode for multiline input", true),
- (".help", "Print this help message", false),
- (".exit", "Exit the REPL", false),
+pub const REPL_COMMANDS: [(&str, &str); 11] = [
+ (".info", "Print the information"),
+ (".set", "Modify the configuration temporarily"),
+ (".prompt", "Add a GPT prompt"),
+ (".role", "Select a role"),
+ (".clear role", "Clear the currently selected role"),
+ (".conversation", "Start a conversation."),
+ (".clear conversation", "End current conversation."),
+ (".history", "Print the history"),
+ (".clear history", "Clear the history"),
+ (".help", "Print this help message"),
+ (".exit", "Exit the REPL"),
];
impl Repl {
@@ -85,14 +85,9 @@ impl Repl {
}
fn handle_line(&mut self, handler: Arc<ReplCmdHandler>, line: String) -> Result<bool> {
- let mut trimed_line = line.trim_start();
- if trimed_line.starts_with('.') {
- trimed_line = trimed_line.trim_end();
- let (cmd, args) = match trimed_line.split_once(' ') {
- Some((head, tail)) => (head, Some(tail)),
- None => (trimed_line, None),
- };
- match cmd {
+ let line = clean_multiline_symbols(&line);
+ match parse_command(&line) {
+ Some((cmd, args)) => match cmd {
".exit" => {
return Ok(true);
}
@@ -121,28 +116,14 @@ impl Repl {
".info" => {
handler.handle(ReplCmd::ViewInfo)?;
}
- ".editor" => {
- let mut text = args.unwrap_or_default().to_string();
- if text.is_empty() {
- print_now!("Usage: .editor {{ <your multiline/paste content here> }}\n\n");
- } else {
- if text.starts_with('{') && text.ends_with('}') {
- text = text[1..text.len() - 1].to_string()
- }
- handler.handle(ReplCmd::Submit(text))?;
- }
- }
".set" => {
handler.handle(ReplCmd::UpdateConfig(args.unwrap_or_default().to_string()))?
}
".prompt" => {
- let mut text = args.unwrap_or_default().to_string();
+ let text = args.unwrap_or_default().to_string();
if text.is_empty() {
- print_now!("Usage: .prompt {{ <your content here> }}.\n\n");
+ print_now!("Usage: .prompt <text>.\n\n");
} else {
- if text.starts_with('{') && text.ends_with('}') {
- text = text[1..text.len() - 1].to_string()
- }
handler.handle(ReplCmd::Prompt(text))?;
}
}
@@ -150,9 +131,10 @@ impl Repl {
handler.handle(ReplCmd::StartConversation)?;
}
_ => dump_unknown_command(),
+ },
+ None => {
+ handler.handle(ReplCmd::Submit(line.to_string()))?;
}
- } else {
- handler.handle(ReplCmd::Submit(line))?;
}
Ok(false)
@@ -166,7 +148,7 @@ fn dump_unknown_command() {
fn dump_repl_help() {
let head = REPL_COMMANDS
.iter()
- .map(|(name, desc, _)| format!("{name:<24} {desc}"))
+ .map(|(name, desc)| format!("{name:<24} {desc}"))
.collect::<Vec<String>>()
.join("\n");
print_now!(
@@ -174,3 +156,56 @@ fn dump_repl_help() {
head,
);
}
+
+fn clean_multiline_symbols(line: &str) -> Cow<str> {
+ let trimed_line = line.trim();
+ match trimed_line.chars().next() {
+ Some('{') | Some('[') | Some('(') => trimed_line[1..trimed_line.len() - 1].into(),
+ _ => Cow::Borrowed(line),
+ }
+}
+
+fn parse_command(line: &str) -> Option<(&str, Option<&str>)> {
+ let mut trimed_line = line.trim_start();
+ if trimed_line.starts_with('.') {
+ trimed_line = trimed_line.trim_end();
+ match trimed_line
+ .split_once(' ')
+ .or_else(|| trimed_line.split_once('\n'))
+ {
+ Some((head, tail)) => {
+ let trimed_tail = tail.trim();
+ if trimed_tail.is_empty() {
+ Some((head, None))
+ } else {
+ Some((head, Some(trimed_tail)))
+ }
+ }
+ None => Some((trimed_line, None)),
+ }
+ } else {
+ None
+ }
+}
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_process_command_line() {
+ assert_eq!(parse_command(" .role"), Some((".role", None)));
+ assert_eq!(parse_command(" .role "), Some((".role", None)));
+ assert_eq!(
+ parse_command(" .set dry_run true"),
+ Some((".set", Some("dry_run true")))
+ );
+ assert_eq!(
+ parse_command(" .set dry_run true "),
+ Some((".set", Some("dry_run true")))
+ );
+ assert_eq!(
+ parse_command(".prompt \nabc\n"),
+ Some((".prompt", Some("abc")))
+ );
+ }
+}
diff --git a/src/repl/prompt.rs b/src/repl/prompt.rs
index 4c56626..dd32ff8 100644
--- a/src/repl/prompt.rs
+++ b/src/repl/prompt.rs
@@ -3,8 +3,6 @@ use crate::config::SharedConfig;
use reedline::{Prompt, PromptHistorySearch, PromptHistorySearchStatus};
use std::borrow::Cow;
-const DEFAULT_MULTILINE_INDICATOR: &str = "::: ";
-
#[derive(Clone)]
pub struct ReplPrompt(SharedConfig);
@@ -43,7 +41,7 @@ impl Prompt for ReplPrompt {
}
fn render_prompt_multiline_indicator(&self) -> Cow<str> {
- Cow::Borrowed(DEFAULT_MULTILINE_INDICATOR)
+ Cow::Borrowed("")
}
fn render_prompt_history_search_indicator(