From 7a60a3636780974dd4dfc7e73e4df0d5ab2f7f93 Mon Sep 17 00:00:00 2001 From: sigoden Date: Sun, 5 Mar 2023 05:50:30 +0800 Subject: feat: add `.info` command (#11) --- README.md | 23 ++++++++++---------- src/config.rs | 9 ++++++-- src/repl.rs | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 77 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 013be65..e7d418b 100644 --- a/README.md +++ b/README.md @@ -52,18 +52,17 @@ no_highlight: false # optional, Whether to disable highlight proxy: "socks5://127.0.0.1:1080" # optional, set proxy server. e.g. http://127.0.0.1:8080 or socks5://127.0.0.1:1080 ``` -The default config dir is as follows, You can override config dir with `$AICHAT_CONFIG_DIR`. - -- Linux: `/home/alice/.config/aichat` -- Windows: `C:\Users\Alice\AppData\Roaming\aichat` -- MacOS: `/Users/Alice/Library/Application Support` - -aichat may generate the following files in the config dir: - -- `config.yaml`: configuration file. -- `roles.yaml`: role definition file. -- `history.txt`: repl history file. -- `messages.md`: chat messages file. +Use `.info` command to view the configuration file path. +``` +〉.info +config file /home/alice/.config/aichat/config.yaml +roles file /home/alice/.config/aichat/roles.yaml +messages file /home/alice/.config/aichat/messages.md +current role +proxy +save messages true +highlight true +``` ### Roles diff --git a/src/config.rs b/src/config.rs index 1c6abc5..b5220dd 100644 --- a/src/config.rs +++ b/src/config.rs @@ -55,7 +55,7 @@ impl Config { self.roles.iter().find(|v| v.name == name).cloned() } - pub fn local_file(name: &str) -> Result { + pub fn config_dir() -> Result { let env_name = format!( "{}_CONFIG_DIR", env!("CARGO_CRATE_NAME").to_ascii_uppercase() @@ -70,6 +70,11 @@ impl Config { anyhow!("Failed to create config dir at {}, {err}", path.display()) })?; } + Ok(path) + } + + pub fn local_file(name: &str) -> Result { + let mut path = Self::config_dir()?; path.push(name); Ok(path) } @@ -117,7 +122,7 @@ impl Config { Self::local_file(HISTORY_FILE_NAME) } - fn messages_file() -> Result { + pub fn messages_file() -> Result { Self::local_file(MESSAGE_FILE_NAME) } diff --git a/src/repl.rs b/src/repl.rs index 81a5474..2a3a48f 100644 --- a/src/repl.rs +++ b/src/repl.rs @@ -13,19 +13,21 @@ use reedline::{ }; use std::cell::RefCell; use std::fs::File; +use std::path::Path; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use std::thread::spawn; -const REPL_COMMANDS: [(&str, &str); 9] = [ +const REPL_COMMANDS: [(&str, &str); 10] = [ (".clear", "Clear the screen"), (".clear-history", "Clear the history"), - (".clear-role", "Clear the role status"), + (".clear-role", "Clear the currently selected role"), (".copy", "Copy last reply message"), (".editor", "Enter multiline editor"), (".exit", "Exit the REPL"), (".help", "Print this help message"), (".history", "Print the history"), + (".info", "Print the information"), (".role", "Specify the role that the AI will play"), ]; @@ -47,7 +49,9 @@ impl Repl { .with_completer(Box::new(completer)) .with_history(history) .with_menu(menu) - .with_edit_mode(edit_mode); + .with_edit_mode(edit_mode) + .with_quick_completions(true) + .with_partial_completions(true); let prompt = Self::create_prompt(); Ok(Self { editor, prompt }) } @@ -150,6 +154,10 @@ impl Repl { dump("Pasted", 1); } } + ".info" => { + handler.handle(ReplCmd::Info)?; + dump("", 1); + } _ => dump_unknown_command(), } } else { @@ -209,8 +217,8 @@ pub struct ReplCmdHandler { } struct ReplCmdHandlerState { - prompt: String, reply: String, + role: Option, save_file: Option, } @@ -221,11 +229,10 @@ impl ReplCmdHandler { } else { Some(Arc::new(MarkdownRender::init()?)) }; - let prompt = role.map(|v| v.prompt).unwrap_or_default(); let save_file = config.open_message_file()?; let ctrlc = Arc::new(AtomicBool::new(false)); let state = RefCell::new(ReplCmdHandlerState { - prompt, + role, save_file, reply: String::new(), }); @@ -244,7 +251,13 @@ impl ReplCmdHandler { self.state.borrow_mut().reply.clear(); return Ok(()); } - let prompt = self.state.borrow().prompt.to_string(); + let prompt = self + .state + .borrow() + .role + .as_ref() + .map(|v| v.prompt.to_string()) + .unwrap_or_default(); let prompt = if prompt.is_empty() { None } else { @@ -274,8 +287,8 @@ impl ReplCmdHandler { self.state.borrow_mut().reply = receiver.output; } ReplCmd::SetRole(name) => match self.config.find_role(&name) { - Some(v) => { - self.state.borrow_mut().prompt = v.prompt; + Some(role) => { + self.state.borrow_mut().role = Some(role); dump("", 1); } None => { @@ -283,7 +296,42 @@ impl ReplCmdHandler { } }, ReplCmd::UnsetRole => { - self.state.borrow_mut().prompt = String::new(); + self.state.borrow_mut().role = None; + } + ReplCmd::Info => { + let state = self.state.borrow(); + let file_info = |path: &Path| { + let state = if path.exists() { "" } else { " [not found]" }; + format!("{}{state}", path.display()) + }; + let items = vec![ + ("config file", file_info(&Config::config_file()?)), + ("roles file", file_info(&Config::roles_file()?)), + ("messages file", file_info(&Config::messages_file()?)), + ( + "current role", + state + .role + .as_ref() + .map(|v| v.name.to_string()) + .unwrap_or_default(), + ), + ( + "proxy", + self.config + .proxy + .as_ref() + .map(|v| v.to_string()) + .unwrap_or_default(), + ), + ("save messages", self.config.save.to_string()), + ("highlight", (!self.config.no_highlight).to_string()), + ]; + let mut info = String::new(); + for (name, value) in items { + info.push_str(&format!("{name:<20}{value}\n")); + } + dump(info, 0); } } Ok(()) @@ -340,6 +388,7 @@ enum ReplCmd { UnsetRole, Input(String), SetRole(String), + Info, } fn dump_repl_help() { -- cgit v1.2.3