返回 DeepSeek-TUI-2026
rename.rs
根目录 / crates / tui / src / commands / rename.rs
1 //! `/rename` command — set a custom title for the current session.
2
3 use crate::session_manager::{SessionManager, update_session};
4 use crate::tui::app::App;
5
6 use super::CommandResult;
7
8 const MAX_TITLE_LEN: usize = 100;
9
10 /// Rename the current session to the given title.
11 ///
12 /// Usage: `/rename <new title>`
13 ///
14 /// The new title is persisted immediately to `~/.deepseek/sessions/<id>.json`
15 /// so the updated name is visible the next time the session picker is opened.
16 pub fn rename(app: &mut App, arg: Option<&str>) -> CommandResult {
17 let new_title = match arg.map(str::trim).filter(|s| !s.is_empty()) {
18 Some(t) => t,
19 None => return CommandResult::error("Usage: /rename <new title>"),
20 };
21
22 if new_title.chars().count() > MAX_TITLE_LEN {
23 return CommandResult::error(format!("Title too long (max {MAX_TITLE_LEN} characters)"));
24 }
25
26 let session_id = match &app.current_session_id {
27 Some(id) => id.clone(),
28 None => {
29 return CommandResult::error(
30 "No active session. Send a message first to start a session.",
31 );
32 }
33 };
34
35 let manager = match SessionManager::default_location() {
36 Ok(m) => m,
37 Err(e) => return CommandResult::error(format!("Could not open sessions directory: {e}")),
38 };
39
40 rename_with_manager(new_title, &session_id, &manager, app)
41 }
42
43 fn rename_with_manager(
44 new_title: &str,
45 session_id: &str,
46 manager: &SessionManager,
47 app: &App,
48 ) -> CommandResult {
49 let mut session = match manager.load_session(session_id) {
50 Ok(s) => s,
51 Err(e) => return CommandResult::error(format!("Could not load session: {e}")),
52 };
53
54 // Sync with current App state to avoid overwriting unsaved messages.
55 session = update_session(
56 session,
57 &app.api_messages,
58 u64::from(app.session.total_tokens),
59 app.system_prompt.as_ref(),
60 );
61 session.metadata.title = new_title.to_string();
62
63 match manager.save_session(&session) {
64 Ok(_) => CommandResult::message(format!("Session renamed to \"{new_title}\"")),
65 Err(e) => CommandResult::error(format!("Could not save session: {e}")),
66 }
67 }
68
69 #[cfg(test)]
70 mod tests {
71 use super::*;
72 use crate::config::Config;
73 use crate::session_manager::{SessionManager, create_saved_session_with_mode};
74 use crate::tui::app::{App, TuiOptions};
75 use tempfile::TempDir;
76
77 fn make_app(tmpdir: &TempDir) -> App {
78 App::new(
79 TuiOptions {
80 model: "deepseek-v4-pro".to_string(),
81 workspace: tmpdir.path().to_path_buf(),
82 config_path: None,
83 config_profile: None,
84 allow_shell: false,
85 use_alt_screen: true,
86 use_mouse_capture: false,
87 use_bracketed_paste: true,
88 max_subagents: 1,
89 skills_dir: tmpdir.path().join("skills"),
90 memory_path: tmpdir.path().join("memory.md"),
91 notes_path: tmpdir.path().join("notes.txt"),
92 mcp_config_path: tmpdir.path().join("mcp.json"),
93 use_memory: false,
94 start_in_agent_mode: false,
95 skip_onboarding: true,
96 yolo: false,
97 resume_session_id: None,
98 initial_input: None,
99 },
100 &Config::default(),
101 )
102 }
103
104 fn make_session_manager(tmpdir: &TempDir) -> SessionManager {
105 SessionManager::new(tmpdir.path().join("sessions")).unwrap()
106 }
107
108 #[test]
109 fn rename_without_arg_returns_error() {
110 let tmp = TempDir::new().unwrap();
111 let mut app = make_app(&tmp);
112 let r = rename(&mut app, None);
113 assert!(r.is_error);
114 assert!(r.message.unwrap().contains("Usage:"));
115 }
116
117 #[test]
118 fn rename_with_empty_arg_returns_error() {
119 let tmp = TempDir::new().unwrap();
120 let mut app = make_app(&tmp);
121 let r = rename(&mut app, Some(" "));
122 assert!(r.is_error);
123 assert!(r.message.unwrap().contains("Usage:"));
124 }
125
126 #[test]
127 fn rename_without_active_session_returns_error() {
128 let tmp = TempDir::new().unwrap();
129 let mut app = make_app(&tmp);
130 app.current_session_id = None;
131 let r = rename(&mut app, Some("My Session"));
132 assert!(r.is_error);
133 assert!(r.message.unwrap().contains("No active session"));
134 }
135
136 #[test]
137 fn rename_title_too_long_returns_error() {
138 let tmp = TempDir::new().unwrap();
139 let mut app = make_app(&tmp);
140 let long_title = "a".repeat(MAX_TITLE_LEN + 1);
141 let r = rename(&mut app, Some(&long_title));
142 assert!(r.is_error);
143 assert!(r.message.unwrap().contains("too long"));
144 }
145
146 #[test]
147 fn rename_persists_new_title() {
148 let tmp = TempDir::new().unwrap();
149 let manager = make_session_manager(&tmp);
150 let app = make_app(&tmp);
151
152 let session =
153 create_saved_session_with_mode(&[], "deepseek-v4-pro", tmp.path(), 0, None, None);
154 let session_id = session.metadata.id.clone();
155 manager.save_session(&session).unwrap();
156
157 let result = rename_with_manager("Brand New Title", &session_id, &manager, &app);
158 assert!(!result.is_error);
159 assert!(result.message.unwrap().contains("Brand New Title"));
160
161 let reloaded = manager.load_session(&session_id).unwrap();
162 assert_eq!(reloaded.metadata.title, "Brand New Title");
163 }
164
165 #[test]
166 fn rename_title_at_max_length_succeeds() {
167 let tmp = TempDir::new().unwrap();
168 let manager = make_session_manager(&tmp);
169 let app = make_app(&tmp);
170
171 let session =
172 create_saved_session_with_mode(&[], "deepseek-v4-pro", tmp.path(), 0, None, None);
173 let session_id = session.metadata.id.clone();
174 manager.save_session(&session).unwrap();
175
176 let max_title = "中".repeat(MAX_TITLE_LEN);
177 let result = rename_with_manager(&max_title, &session_id, &manager, &app);
178 assert!(!result.is_error);
179
180 let reloaded = manager.load_session(&session_id).unwrap();
181 assert_eq!(reloaded.metadata.title, max_title);
182 }
183 }
184
184 lines RUST