diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-05-31 21:18:40 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-05-31 21:18:40 +0300 |
| commit | 8cf0e9b35b00f9dd91270fcd3931015d61ffd8a0 (patch) | |
| tree | 2b76b158344fdbe0de17c6abe98564a262266d2f /src/app/input | |
| parent | 859185b491c5a80e888ba8f9dd213d2c35baa034 (diff) | |
fix: confirm implicit worktree group close
refs #369
Diffstat (limited to 'src/app/input')
| -rw-r--r-- | src/app/input/modal.rs | 64 | ||||
| -rw-r--r-- | src/app/input/mouse.rs | 36 | ||||
| -rw-r--r-- | src/app/input/navigate.rs | 26 |
3 files changed, 96 insertions, 30 deletions
diff --git a/src/app/input/modal.rs b/src/app/input/modal.rs index 6cde98b5..e85e9bb1 100644 --- a/src/app/input/modal.rs +++ b/src/app/input/modal.rs @@ -695,12 +695,13 @@ pub(super) fn apply_context_menu_action( state.selected = ws_idx; state.active = Some(ws_idx); state.switch_tab(tab_idx); - state.close_tab(); - state.mode = if state.active.is_some() { - Mode::Terminal - } else { - Mode::Navigate - }; + if !state.close_tab() { + state.mode = if state.active.is_some() { + Mode::Terminal + } else { + Mode::Navigate + }; + } } (ContextMenuKind::Pane { pane_id, .. }, Some("Rename pane")) => { open_rename_pane(state, pane_id); @@ -732,12 +733,13 @@ pub(super) fn apply_context_menu_action( state.mode = Mode::Terminal; } (ContextMenuKind::Pane { .. }, Some("Close pane")) => { - state.close_pane(); - state.mode = if state.active.is_some() { - Mode::Terminal - } else { - Mode::Navigate - }; + if !state.close_pane() { + state.mode = if state.active.is_some() { + Mode::Terminal + } else { + Mode::Navigate + }; + } } _ => leave_modal(state), } @@ -1260,4 +1262,42 @@ mod tests { assert!(state.workspaces.is_empty()); assert_eq!(state.mode, Mode::Navigate); } + + #[test] + fn context_menu_close_pane_last_parent_group_pane_keeps_confirmation_mode() { + let mut state = state_with_workspaces(&["main", "issue"]); + state.active = Some(0); + state.selected = 1; + state.workspaces[0].worktree_space = Some(crate::workspace::WorktreeSpaceMembership { + key: "repo-key".into(), + label: "herdr".into(), + repo_root: "/repo/herdr".into(), + checkout_path: "/repo/herdr".into(), + is_linked_worktree: false, + }); + state.workspaces[1].worktree_space = Some(crate::workspace::WorktreeSpaceMembership { + key: "repo-key".into(), + label: "herdr".into(), + repo_root: "/repo/herdr".into(), + checkout_path: "/repo/herdr-issue".into(), + is_linked_worktree: true, + }); + let pane_id = state.workspaces[0].tabs[0].root_pane; + let menu = ContextMenuState { + kind: ContextMenuKind::Pane { + pane_id, + has_manual_label: false, + }, + x: 0, + y: 0, + list: MenuListState::new(4), + }; + let mut terminal_runtimes = crate::terminal::TerminalRuntimeRegistry::new(); + + apply_context_menu_action(&mut state, &mut terminal_runtimes, menu, 4); + + assert_eq!(state.selected, 0); + assert_eq!(state.mode, Mode::ConfirmClose); + assert_eq!(state.workspaces.len(), 2); + } } diff --git a/src/app/input/mouse.rs b/src/app/input/mouse.rs index e3ad0bc5..cc03c8f6 100644 --- a/src/app/input/mouse.rs +++ b/src/app/input/mouse.rs @@ -145,14 +145,13 @@ impl AppState { match mouse.kind { MouseEventKind::ScrollUp => { if let Some(open) = &mut self.worktree_open { - open.selected = open.selected.saturating_sub(1); + open.select_previous_filtered(); } return None; } MouseEventKind::ScrollDown => { if let Some(open) = &mut self.worktree_open { - open.selected = - (open.selected + 1).min(open.entries.len().saturating_sub(1)); + open.select_next_filtered(); } return None; } @@ -237,21 +236,28 @@ impl AppState { self.screen_rect(), open.entries.len(), ) { - let max_rows = inner.height.saturating_sub(4) as usize; - let start = crate::ui::open_existing_worktree_visible_start( - open.selected, - max_rows, - ); + let filtered = open.filtered_indices(); + let max_rows = + crate::ui::open_existing_worktree_max_visible_rows(inner); + let start = + crate::ui::open_existing_worktree_visible_start(open, max_rows); + if mouse.row == inner.y.saturating_add(1) + && mouse.column >= inner.x + && mouse.column < inner.x.saturating_add(inner.width) + { + if let Some(open) = &mut self.worktree_open { + open.search_focused = true; + } + return None; + } let row_idx = if rect_contains(inner, mouse.column, mouse.row) { mouse .row - .checked_sub(inner.y.saturating_add(2)) + .checked_sub(inner.y.saturating_add(3)) .map(usize::from) + .map(|row| row / 2) .filter(|row| *row < max_rows) - .and_then(|row| { - let entry_idx = start + row; - (entry_idx < open.entries.len()).then_some(entry_idx) - }) + .and_then(|row| filtered.get(start + row).copied()) } else { None }; @@ -1657,6 +1663,8 @@ mod tests { }, ], selected: 0, + query: String::new(), + search_focused: false, error: None, } } @@ -1810,7 +1818,7 @@ mod tests { app.handle_mouse(mouse( MouseEventKind::Down(MouseButton::Left), inner.x + 1, - inner.y + 3, + inner.y + 5, )); assert_eq!(app.state.worktree_open.as_ref().unwrap().selected, 1); diff --git a/src/app/input/navigate.rs b/src/app/input/navigate.rs index 8d09c00a..b6edefaf 100644 --- a/src/app/input/navigate.rs +++ b/src/app/input/navigate.rs @@ -761,8 +761,9 @@ pub(super) fn execute_navigate_action_in_context( leave_navigate_mode(state); } NavigateAction::CloseTab => { - state.close_tab(); - leave_navigate_mode(state); + if !state.close_tab() { + leave_navigate_mode(state); + } } NavigateAction::RenamePane => { if let Some(pane_id) = state @@ -786,8 +787,9 @@ pub(super) fn execute_navigate_action_in_context( leave_navigate_mode(state); } NavigateAction::ClosePane => { - state.close_pane(); - leave_navigate_mode(state); + if !state.close_pane() { + leave_navigate_mode(state); + } } NavigateAction::EditScrollback => {} NavigateAction::CopyMode => state.enter_copy_mode(terminal_runtimes), @@ -1756,6 +1758,22 @@ last_pane = "prefix+tab" assert_eq!(state.mode, Mode::Terminal); } + #[test] + fn prefix_close_pane_last_parent_group_pane_opens_confirmation() { + let mut state = state_with_workspaces(&["main", "issue"]); + mark_worktree_space_member(&mut state, 0, "repo-key"); + mark_worktree_space_member(&mut state, 1, "repo-key"); + state.selected = 1; + state.active = Some(0); + state.mode = Mode::Navigate; + + execute_navigate_action(&mut state, NavigateAction::ClosePane); + + assert_eq!(state.selected, 0); + assert_eq!(state.mode, Mode::ConfirmClose); + assert_eq!(state.workspaces.len(), 2); + } + #[tokio::test] async fn custom_command_runs_from_prefix_key_in_navigate_mode() { let (_api_tx, api_rx) = tokio::sync::mpsc::unbounded_channel(); |
