Skip to main content

git_tailor/views/
squash_select.rs

1// Copyright 2026 Thomas Johannesson
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15// Squash target selection — key handling only; rendering is done via the
16// commit list footer (see `render_footer` in commit_list.rs).
17
18use crate::app::{AppAction, AppMode, AppState, KeyCommand};
19
20/// Handle an action while in SquashSelect mode.
21///
22/// The user navigates the commit list to pick a squash target. The source
23/// commit (from `source_index`) will be squashed *into* the chosen target.
24/// Navigation is clamped so the cursor cannot move to commits later than
25/// the source — squashing into a later commit is not supported.
26pub fn handle_key(action: KeyCommand, app: &mut AppState) -> AppAction {
27    let (source_index, is_fixup) = match app.mode {
28        AppMode::SquashSelect {
29            source_index,
30            is_fixup,
31        } => (source_index, is_fixup),
32        _ => return AppAction::Handled,
33    };
34
35    match action {
36        KeyCommand::MoveUp => {
37            if app.reverse {
38                app.move_down();
39            } else {
40                app.move_up();
41            }
42            app.selection_index = app.selection_index.min(source_index);
43            AppAction::Handled
44        }
45        KeyCommand::MoveDown => {
46            if app.reverse {
47                app.move_up();
48            } else {
49                app.move_down();
50            }
51            app.selection_index = app.selection_index.min(source_index);
52            AppAction::Handled
53        }
54        KeyCommand::PageUp => {
55            let h = app.commit_list_visible_height;
56            if app.reverse {
57                app.page_down(h);
58            } else {
59                app.page_up(h);
60            }
61            app.selection_index = app.selection_index.min(source_index);
62            AppAction::Handled
63        }
64        KeyCommand::PageDown => {
65            let h = app.commit_list_visible_height;
66            if app.reverse {
67                app.page_up(h);
68            } else {
69                app.page_down(h);
70            }
71            app.selection_index = app.selection_index.min(source_index);
72            AppAction::Handled
73        }
74        KeyCommand::Confirm => {
75            let target_index = app.selection_index;
76
77            // Cannot squash onto itself
78            if target_index == source_index {
79                app.set_error_message("Cannot squash a commit into itself");
80                return AppAction::Handled;
81            }
82
83            // Cannot squash onto staged/unstaged
84            let target = &app.commits[target_index];
85            if target.oid == "staged" || target.oid == "unstaged" {
86                app.set_error_message("Cannot squash into staged/unstaged changes");
87                return AppAction::Handled;
88            }
89
90            let source = &app.commits[source_index];
91            let result = AppAction::PrepareSquash {
92                source_oid: source.oid.clone(),
93                target_oid: target.oid.clone(),
94                source_message: source.message.clone(),
95                target_message: target.message.clone(),
96                is_fixup,
97            };
98
99            app.mode = AppMode::CommitList;
100            result
101        }
102        KeyCommand::ShowHelp => {
103            app.toggle_help();
104            AppAction::Handled
105        }
106        KeyCommand::Quit => {
107            app.cancel_squash_select();
108            AppAction::Handled
109        }
110        _ => AppAction::Handled,
111    }
112}