quinjet 0.0.51

A fast, live, keyboard-first Git source-control interface for the terminal
#[cfg_attr(not(test), expect(clippy::wildcard_imports, reason = "shared"))]
use super::*;

impl App {
    #[expect(
        clippy::too_many_lines,
        reason = "the exhaustive modal match keeps each state transition together"
    )]
    pub(super) fn handle_picker_modal_key(
        &mut self,
        mut modal: Modal,
        key: KeyEvent,
        now: Instant,
    ) -> Vec<AppEffect> {
        let mut effects = Vec::new();
        match &mut modal {
            Modal::Projects {
                groups,
                selected,
                query,
                collapsed,
                loading,
                opening,
                mode,
            } => {
                if key.code == KeyCode::Esc {
                    if *mode == ProjectOpenMode::NewTab {
                        effects.push(AppEffect::CancelRepositoryTabPicker);
                    }
                    return effects;
                }
                if matches!(key.code, KeyCode::Char('e' | 'E'))
                    && key.modifiers.contains(KeyModifiers::CONTROL)
                {
                    Self::toggle_all_project_groups(groups, collapsed);
                    *selected = 0;
                    self.remember_collapsed_project_groups(collapsed);
                    self.modal = Some(modal);
                    return effects;
                }
                if matches!(key.code, KeyCode::Tab | KeyCode::BackTab)
                    && let Some(context) = self.ssh_context.as_ref()
                {
                    let reverse =
                        key.code == KeyCode::BackTab || key.modifiers.contains(KeyModifiers::SHIFT);
                    if let Some(index) = context.adjacent_accessible_machine_index(reverse)
                        && let Some(effect) = machine_switch_effect(context, index, *mode)
                    {
                        effects.push(effect);
                    }
                    self.modal = Some(modal);
                    return effects;
                }
                let visible = Self::filtered_project_rows(groups, &query.value, collapsed);
                let selected_tree = visible.get(*selected).and_then(|row| match row {
                    ProjectRow::Group(_) => None,
                    ProjectRow::Worktree {
                        group_index,
                        tree_index,
                    } => groups
                        .get(*group_index)
                        .and_then(|group| group.worktrees.get(*tree_index))
                        .cloned(),
                });
                let selected_group = visible.get(*selected).and_then(|row| match row {
                    ProjectRow::Group(group_index) | ProjectRow::Worktree { group_index, .. } => {
                        groups.get(*group_index).cloned()
                    }
                });
                match key.code {
                    KeyCode::Up | KeyCode::Char('k') => {
                        *selected = previous_list_index(*selected, visible.len());
                    }
                    KeyCode::Down | KeyCode::Char('j') => {
                        *selected = next_list_index(*selected, visible.len());
                    }
                    KeyCode::Left | KeyCode::Char('h') => {
                        if let Some(group) = selected_group
                            && !collapsed.contains(&group.common_dir)
                        {
                            let common_dir = group.common_dir;
                            collapsed.extend([common_dir.clone()]);
                            *selected = Self::filtered_project_rows(
                                groups,
                                &query.value,
                                collapsed,
                            )
                            .iter()
                            .position(|row| {
                                matches!(row, ProjectRow::Group(index) if groups.get(*index).is_some_and(|candidate| candidate.common_dir == common_dir))
                            })
                            .unwrap_or_default();
                            self.remember_collapsed_project_groups(collapsed);
                        }
                    }
                    KeyCode::Right | KeyCode::Char('l') => {
                        if let Some(group) = selected_group
                            && collapsed.remove(&group.common_dir)
                        {
                            self.remember_collapsed_project_groups(collapsed);
                        }
                    }
                    KeyCode::Char(' ') => {
                        if let Some(ProjectRow::Group(group_index)) = visible.get(*selected)
                            && let Some(group) = groups.get(*group_index)
                        {
                            toggle_membership(collapsed, group.common_dir.clone());
                            self.remember_collapsed_project_groups(collapsed);
                        }
                    }
                    KeyCode::Enter if !*loading && opening.is_none() => {
                        if let Some(ProjectRow::Group(group_index)) = visible.get(*selected)
                            && let Some(group) = groups.get(*group_index)
                        {
                            toggle_membership(collapsed, group.common_dir.clone());
                            self.remember_collapsed_project_groups(collapsed);
                            self.modal = Some(modal);
                            return effects;
                        }
                        if let Some(tree) = selected_tree.filter(|tree| !tree.current) {
                            *opening = Some(tree.path.clone());
                            effects.push(match mode {
                                ProjectOpenMode::Initial | ProjectOpenMode::CurrentTab => {
                                    AppEffect::SwitchRepository(tree.path)
                                }
                                ProjectOpenMode::NewTab => AppEffect::OpenRepositoryTab(tree.path),
                            });
                        }
                        self.modal = Some(modal);
                        return effects;
                    }
                    KeyCode::Delete if !*loading => {
                        if let Some(group) = selected_group
                            .filter(|group| !group.worktrees.iter().any(|tree| tree.current))
                        {
                            crate::state::forget_recent_project(&group.common_dir);
                            self.request_recent_projects(&mut effects);
                        }
                        self.modal = Some(modal);
                        return effects;
                    }
                    _ => {
                        edit_text(query, key, false);
                        *selected = 0;
                    }
                }
                self.modal = Some(modal);
            }
            Modal::PullRequestRepositories {
                items,
                selected,
                query,
                loading,
            } => {
                if key.code == KeyCode::Esc {
                    return effects;
                }
                let visible = Self::filtered_github_repositories(items, &query.value);
                match key.code {
                    KeyCode::Up | KeyCode::Char('k') => {
                        *selected = previous_list_index(*selected, visible.len());
                    }
                    KeyCode::Down | KeyCode::Char('j') => {
                        *selected = next_list_index(*selected, visible.len());
                    }
                    KeyCode::Enter if !*loading => {
                        if let Some(repository) = visible
                            .get(*selected)
                            .and_then(|index| items.get(*index))
                            .cloned()
                        {
                            self.pull_request_repository = Some(repository);
                            if let Some(number) = self.pull_request_exact_number.or_else(|| {
                                self.pull_request_lookup.value.trim().parse::<u64>().ok()
                            }) {
                                self.request_pull_request_lookup(
                                    number,
                                    false,
                                    false,
                                    &mut effects,
                                );
                            }
                        }
                        return effects;
                    }
                    _ => {
                        edit_text(query, key, false);
                        *selected = 0;
                    }
                }
                self.modal = Some(modal);
            }
            Modal::CommandPalette { query, selected } => {
                if key.code == KeyCode::Esc {
                    return effects;
                }
                let commands = self.palette_commands(&query.value);
                match key.code {
                    KeyCode::Up => *selected = previous_list_index(*selected, commands.len()),
                    KeyCode::Down => *selected = next_list_index(*selected, commands.len()),
                    KeyCode::Enter => {
                        if let Some(command) = commands.get(*selected).copied() {
                            self.execute_palette(command, &mut effects, now);
                        }
                        return effects;
                    }
                    _ => {
                        edit_text(query, key, false);
                        *selected = 0;
                    }
                }
                self.modal = Some(modal);
            }
            Modal::Themes { selected, original } => {
                match key.code {
                    KeyCode::Esc => {
                        self.apply_theme(*original);
                        return effects;
                    }
                    KeyCode::Up | KeyCode::Char('k') => {
                        *selected = previous_list_index(*selected, ThemeName::ALL.len());
                        if let Some(name) = ThemeName::ALL.get(*selected).copied() {
                            self.apply_theme(name);
                        }
                    }
                    KeyCode::Down | KeyCode::Char('j') => {
                        *selected = next_list_index(*selected, ThemeName::ALL.len());
                        if let Some(name) = ThemeName::ALL.get(*selected).copied() {
                            self.apply_theme(name);
                        }
                    }
                    KeyCode::Enter => {
                        if let Some(name) = ThemeName::ALL.get(*selected).copied() {
                            self.apply_theme(name);
                            self.show_toast(
                                format!("Theme changed to {}", name.label()),
                                ToastLevel::Success,
                                now,
                            );
                        }
                        return effects;
                    }
                    _ => {}
                }
                self.modal = Some(modal);
            }
            Modal::Appearances {
                selected,
                original_choice,
                original_appearance,
            } => {
                match key.code {
                    KeyCode::Esc => {
                        self.appearance_choice = *original_choice;
                        self.appearance = *original_appearance;
                        self.theme = Theme::new(self.theme_name, self.appearance);
                        return effects;
                    }
                    KeyCode::Up | KeyCode::Char('k') => {
                        *selected = previous_list_index(*selected, AppearanceChoice::ALL.len());
                        if let Some(choice) = AppearanceChoice::ALL.get(*selected).copied() {
                            self.set_theme_selection(self.theme_name, choice);
                        }
                    }
                    KeyCode::Down | KeyCode::Char('j') => {
                        *selected = next_list_index(*selected, AppearanceChoice::ALL.len());
                        if let Some(choice) = AppearanceChoice::ALL.get(*selected).copied() {
                            self.set_theme_selection(self.theme_name, choice);
                        }
                    }
                    KeyCode::Enter => {
                        if let Some(choice) = AppearanceChoice::ALL.get(*selected).copied() {
                            self.set_theme_selection(self.theme_name, choice);
                            self.show_toast(
                                format!("Appearance changed to {}", choice.label()),
                                ToastLevel::Success,
                                now,
                            );
                        }
                        return effects;
                    }
                    _ => {}
                }
                self.modal = Some(modal);
            }
            _ => {}
        }
        effects
    }
}

fn machine_switch_effect(
    context: &SshContext,
    index: usize,
    project_mode: ProjectOpenMode,
) -> Option<AppEffect> {
    let machine = context.machines.get(index)?;
    if !machine.accessible || machine.target == context.current {
        return None;
    }
    let mode = if project_mode == ProjectOpenMode::NewTab {
        crate::ssh::SshProjectOpenMode::New
    } else {
        crate::ssh::SshProjectOpenMode::Current
    };
    Some(AppEffect::SwitchSshMachine(crate::ssh::SshSwitch {
        index,
        mode,
    }))
}