feat: add `.edit session` repl command (#606)

pull/607/head
sigoden 3 months ago committed by GitHub
parent 72e689fab0
commit 189f2271bc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -713,17 +713,42 @@ impl Config {
Ok(())
}
pub fn save_session(&mut self, name: &str) -> Result<()> {
let sessions_dir = self.sessions_dir()?;
pub fn save_session(&mut self, name: Option<&str>) -> Result<()> {
let name = match &self.session {
Some(session) => match name {
Some(v) => v.to_string(),
None => session.name().to_string(),
},
None => bail!("No session"),
};
let session_path = self.session_file(&name)?;
if let Some(session) = self.session.as_mut() {
if !name.is_empty() {
session.set_name(name);
}
session.save(&sessions_dir)?;
session.save(&session_path)?;
}
Ok(())
}
pub fn edit_session(&mut self) -> Result<()> {
let name = match &self.session {
Some(session) => session.name().to_string(),
None => bail!("No session"),
};
let editor = match self.buffer_editor() {
Some(editor) => editor,
None => bail!("No editor, please set $EDITOR/$VISUAL."),
};
let session_path = self.session_file(&name)?;
self.save_session(Some(&name))?;
edit_file(&editor, &session_path).with_context(|| {
format!(
"Failed to edit '{}' with '{editor}'",
session_path.display()
)
})?;
self.session = Some(Session::load(self, &name, &session_path)?);
Ok(())
}
pub fn clear_session_messages(&mut self) -> Result<()> {
if let Some(session) = self.session.as_mut() {
session.clear_messages();

@ -240,10 +240,6 @@ impl Session {
(tokens, percent)
}
pub fn set_name(&mut self, name: &str) {
self.name = name.to_string();
}
pub fn set_role(&mut self, role: Role) {
self.model_id = role.model().id();
self.temperature = role.temperature();
@ -290,7 +286,7 @@ impl Session {
self.dirty = true;
}
pub fn exit(&mut self, sessions_dir: &Path, is_repl: bool) -> Result<()> {
pub fn exit(&mut self, session_dir: &Path, is_repl: bool) -> Result<()> {
let save_session = self.save_session();
if self.dirty && save_session != Some(false) {
if save_session.is_none() {
@ -315,25 +311,26 @@ impl Session {
.prompt()?;
}
}
self.save(sessions_dir)?;
let session_path = session_dir.join(format!("{}.yaml", self.name()));
self.save(&session_path)?;
}
Ok(())
}
pub fn save(&mut self, sessions_dir: &Path) -> Result<()> {
let mut session_path = sessions_dir.to_path_buf();
session_path.push(format!("{}.yaml", self.name()));
if !sessions_dir.exists() {
create_dir_all(sessions_dir).with_context(|| {
format!("Failed to create session_dir '{}'", sessions_dir.display())
})?;
pub fn save(&mut self, session_path: &Path) -> Result<()> {
if let Some(sessions_dir) = session_path.parent() {
if !sessions_dir.exists() {
create_dir_all(sessions_dir).with_context(|| {
format!("Failed to create session_dir '{}'", sessions_dir.display())
})?;
}
}
self.path = Some(session_path.display().to_string());
let content = serde_yaml::to_string(&self)
.with_context(|| format!("Failed to serde session {}", self.name))?;
fs::write(&session_path, content).with_context(|| {
fs::write(session_path, content).with_context(|| {
format!(
"Failed to write session {} to {}",
self.name,

@ -33,7 +33,7 @@ lazy_static! {
const MENU_NAME: &str = "completion_menu";
lazy_static! {
static ref REPL_COMMANDS: [ReplCommand; 23] = [
static ref REPL_COMMANDS: [ReplCommand; 24] = [
ReplCommand::new(".help", "Show this help message", AssertState::pass()),
ReplCommand::new(".info", "View system info", AssertState::pass()),
ReplCommand::new(".model", "Change the current LLM", AssertState::pass()),
@ -72,6 +72,11 @@ lazy_static! {
"Save the chat to file",
AssertState::True(StateFlags::SESSION_EMPTY | StateFlags::SESSION)
),
ReplCommand::new(
".edit session",
"Edit the current session",
AssertState::True(StateFlags::SESSION_EMPTY | StateFlags::SESSION)
),
ReplCommand::new(
".clear messages",
"Erase messages in the current session",
@ -279,8 +284,8 @@ Tips: use <tab> to autocomplete conversation starter text.
},
".save" => {
match args.map(|v| match v.split_once(' ') {
Some((subcmd, args)) => (subcmd, args.trim()),
None => (v, ""),
Some((subcmd, args)) => (subcmd, Some(args.trim())),
None => (v, None),
}) {
Some(("session", name)) => {
self.config.write().save_session(name)?;
@ -290,6 +295,19 @@ Tips: use <tab> to autocomplete conversation starter text.
}
}
}
".edit" => {
match args.map(|v| match v.split_once(' ') {
Some((subcmd, args)) => (subcmd, Some(args.trim())),
None => (v, None),
}) {
Some(("session", _)) => {
self.config.write().edit_session()?;
}
_ => {
println!(r#"Usage: .edit session"#)
}
}
}
".set" => match args {
Some(args) => {
self.config.write().update(args)?;

@ -1,4 +1,4 @@
use std::{collections::HashMap, env, ffi::OsStr, process::Command};
use std::{collections::HashMap, env, ffi::OsStr, path::Path, process::Command};
use anyhow::{Context, Result};
@ -90,3 +90,9 @@ pub fn run_command_with_output<T: AsRef<OsStr>>(
let stderr = std::str::from_utf8(&output.stderr).context("Invalid UTF-8 in stderr")?;
Ok((status.success(), stdout.to_string(), stderr.to_string()))
}
pub fn edit_file(editor: &str, path: &Path) -> Result<()> {
let mut child = Command::new(editor).arg(path).spawn()?;
child.wait()?;
Ok(())
}

Loading…
Cancel
Save