diff --git a/client/config.toml b/client/config.toml index 9698bff..bb1bbca 100644 --- a/client/config.toml +++ b/client/config.toml @@ -4,6 +4,7 @@ enter_command_mode = [":", "ctrl+;"] next_buffer = ["ctrl+l"] previous_buffer = ["ctrl+h"] +close_buffer = ["ctrl+k"] [keybindings.general] move_up = ["k", "Up"] diff --git a/client/src/modes/handlers/event.rs b/client/src/modes/handlers/event.rs index 573779c..45f7dc8 100644 --- a/client/src/modes/handlers/event.rs +++ b/client/src/modes/handlers/event.rs @@ -161,6 +161,7 @@ impl EventHandler { return Ok(EventOutcome::Ok(message)); } + if !matches!(current_mode, AppMode::Edit | AppMode::Command) { if let Some(action) = config.get_action_for_key_in_mode( &config.keybindings.global, key_code, modifiers @@ -176,6 +177,10 @@ impl EventHandler { return Ok(EventOutcome::Ok("Switched to previous buffer".to_string())); } } + "close_buffer" => { + let message = buffer_state.close_buffer_with_intro_fallback(); + return Ok(EventOutcome::Ok(message)); + } _ => {} } } diff --git a/client/src/state/app/buffer.rs b/client/src/state/app/buffer.rs index 07d3134..83ec5b6 100644 --- a/client/src/state/app/buffer.rs +++ b/client/src/state/app/buffer.rs @@ -1,6 +1,5 @@ // src/state/app/buffer.rs -/// Represents the distinct views or "buffers" the user can navigate. #[derive(Debug, Clone, PartialEq, Eq)] pub enum AppView { Intro, @@ -14,7 +13,6 @@ pub enum AppView { } impl AppView { - /// Returns the display name for the view. pub fn display_name(&self) -> &str { match self { AppView::Intro => "Intro", @@ -29,7 +27,6 @@ impl AppView { } } -/// Holds the state related to buffer management (navigation history). #[derive(Debug, Clone)] pub struct BufferState { pub history: Vec, @@ -39,23 +36,17 @@ pub struct BufferState { impl Default for BufferState { fn default() -> Self { Self { - history: vec![AppView::Intro], // Start with Intro view + history: vec![AppView::Intro], active_index: 0, } } } impl BufferState { - /// Updates the buffer history and active index. - /// If the view already exists, it sets it as active. - /// Otherwise, it adds the new view and makes it active. pub fn update_history(&mut self, view: AppView) { let existing_pos = self.history.iter().position(|v| v == &view); - match existing_pos { - Some(pos) => { - self.active_index = pos; - } + Some(pos) => self.active_index = pos, None => { self.history.push(view.clone()); self.active_index = self.history.len() - 1; @@ -63,34 +54,51 @@ impl BufferState { } } - /// Gets the currently active view. pub fn get_active_view(&self) -> Option<&AppView> { self.history.get(self.active_index) } - /// Removes the currently active buffer from the history, unless it's the Intro buffer. - /// Sets the new active buffer to the one preceding the closed one. - /// # Returns - /// * `true` if a non-Intro buffer was closed. - /// * `false` if the active buffer was Intro or only Intro remained. pub fn close_active_buffer(&mut self) -> bool { - let current_index = self.active_index; + if self.history.is_empty() { + self.history.push(AppView::Intro); + self.active_index = 0; + return false; + } - // Rule 1: Cannot close Intro buffer. + let current_index = self.active_index; if matches!(self.history.get(current_index), Some(AppView::Intro)) { return false; } - // Rule 2: Cannot close if only Intro would remain (or already remains). - // This check implicitly covers the case where len <= 1. - if self.history.len() <= 1 { - return false; - } - self.history.remove(current_index); - self.active_index = current_index.saturating_sub(1).min(self.history.len() - 1); - + if self.history.is_empty() { + self.history.push(AppView::Intro); + self.active_index = 0; + } else if self.active_index >= self.history.len() { + self.active_index = self.history.len() - 1; + } true } -} + pub fn close_buffer_with_intro_fallback(&mut self) -> String { + let current_view_cloned = self.get_active_view().cloned(); + if let Some(AppView::Intro) = current_view_cloned { + return "Cannot close intro buffer".to_string(); + } + + let closed_name = current_view_cloned + .as_ref() + .map(|v| v.display_name().to_string()) + .unwrap_or_else(|| "Unknown".to_string()); + + if self.close_active_buffer() { + if self.history.len() == 1 && matches!(self.history.get(0), Some(AppView::Intro)) { + format!("Closed '{}' - returned to intro", closed_name) + } else { + format!("Closed '{}'", closed_name) + } + } else { + format!("Cannot close buffer: {}", closed_name) + } + } +}