summaryrefslogtreecommitdiffstats
path: root/src/repl/init.rs
diff options
context:
space:
mode:
authorsigoden <sigoden@gmail.com>2023-03-06 06:45:00 +0800
committerGitHub <noreply@github.com>2023-03-06 06:45:00 +0800
commit2152667fc6551129fd0bb14ca9e403728855aea3 (patch)
treeab37ba4ea0f0a127a9f4e116220b57d9a077c36d /src/repl/init.rs
parentd40913d7396e1dc5f3b6ddf47b61467b1e04430f (diff)
downloadaichat-2152667fc6551129fd0bb14ca9e403728855aea3.tar.gz
refactor: split src/repl.rs to submodules (#22)
Diffstat (limited to 'src/repl/init.rs')
-rw-r--r--src/repl/init.rs121
1 files changed, 121 insertions, 0 deletions
diff --git a/src/repl/init.rs b/src/repl/init.rs
new file mode 100644
index 0000000..f640deb
--- /dev/null
+++ b/src/repl/init.rs
@@ -0,0 +1,121 @@
+use super::Repl;
+
+use crate::config::{Config, SharedConfig};
+
+use anyhow::{Context, Result};
+use reedline::{
+ default_emacs_keybindings, ColumnarMenu, DefaultCompleter, DefaultPrompt, DefaultPromptSegment,
+ Emacs, FileBackedHistory, KeyCode, KeyModifiers, Keybindings, Reedline, ReedlineEvent,
+ ReedlineMenu, ValidationResult, Validator,
+};
+
+use super::REPL_COMMANDS;
+
+const MENU_NAME: &str = "completion_menu";
+
+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()?;
+ let menu = Self::create_menu();
+ let edit_mode = Box::new(Emacs::new(keybindings));
+ let editor = Reedline::create()
+ .with_completer(Box::new(completer))
+ .with_history(history)
+ .with_menu(menu)
+ .with_edit_mode(edit_mode)
+ .with_quick_completions(true)
+ .with_partial_completions(true)
+ .with_validator(Box::new(ReplValidator { multiline_commands }))
+ .with_ansi_colors(true);
+ let prompt = Self::create_prompt();
+ Ok(Self { editor, prompt })
+ }
+
+ fn create_prompt() -> DefaultPrompt {
+ DefaultPrompt::new(DefaultPromptSegment::Empty, DefaultPromptSegment::Empty)
+ }
+
+ fn create_completer(config: SharedConfig) -> DefaultCompleter {
+ let mut completion: Vec<String> = REPL_COMMANDS
+ .into_iter()
+ .map(|(v, _, _)| v.to_string())
+ .collect();
+ completion.extend(config.borrow().repl_completions());
+ let mut completer = DefaultCompleter::with_inclusions(&['.', '-', '_']).set_min_word_len(2);
+ completer.insert(completion.clone());
+ completer
+ }
+
+ fn create_keybindings() -> Keybindings {
+ let mut keybindings = default_emacs_keybindings();
+ keybindings.add_binding(
+ KeyModifiers::NONE,
+ KeyCode::Tab,
+ ReedlineEvent::UntilFound(vec![
+ ReedlineEvent::Menu(MENU_NAME.to_string()),
+ ReedlineEvent::MenuNext,
+ ]),
+ );
+ keybindings.add_binding(
+ KeyModifiers::CONTROL,
+ KeyCode::Char('l'),
+ ReedlineEvent::ExecuteHostCommand(".clear screen".into()),
+ );
+ keybindings
+ }
+
+ fn create_menu() -> ReedlineMenu {
+ let completion_menu = ColumnarMenu::default().with_name(MENU_NAME);
+ ReedlineMenu::EngineCompleter(Box::new(completion_menu))
+ }
+
+ fn create_history() -> Result<Box<FileBackedHistory>> {
+ Ok(Box::new(
+ FileBackedHistory::with_file(1000, Config::history_file()?)
+ .with_context(|| "Failed to setup history file")?,
+ ))
+ }
+}
+
+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()
+}