magi-code 0.96.1

Repository-aware CLI coding agent for terminal work
Documentation
use super::*;

pub(super) fn classify_keybinding_action(
    action: KeyCommand,
    state: &MissionControlState,
    prompt_viewport: PromptViewport,
    transcript_viewport: TranscriptViewport,
    activity_tree_viewport: ActivityTreeViewport,
    detail_viewport: DetailViewport,
) -> InputCommand {
    let display = |command: DisplayCommand, refresh_autocomplete: bool| {
        if refresh_autocomplete {
            DisplayCommand::batch([command, DisplayCommand::RefreshAutocomplete])
        } else {
            command
        }
    };
    let has_activity = state.visible_node_count() != 0;
    match action {
        KeyCommand::ToggleHeaderModalsPinned => {
            InputCommand::display(DisplayCommand::ToggleHeaderModalsPinned)
        }
        KeyCommand::OpenActivityDetail => InputCommand::display(DisplayCommand::OpenActivityDetail),
        KeyCommand::ToggleSummaryModal => InputCommand::display(DisplayCommand::ToggleSummaryModal),
        KeyCommand::ToggleActivityTreeModal => {
            InputCommand::display(DisplayCommand::ToggleActivityTreeModal)
        }
        KeyCommand::ToggleSessionFilesModal => {
            InputCommand::display(DisplayCommand::ToggleSessionFilesModal)
        }
        // Summary and session-files keys are classified by `modal::classify_modal_key`;
        // these bindings exist only for the help listing.
        KeyCommand::ToggleSummary
        | KeyCommand::ScrollSummaryUp
        | KeyCommand::ScrollSummaryDown
        | KeyCommand::PageSummaryUp
        | KeyCommand::PageSummaryDown => InputCommand::None,
        KeyCommand::SubmitPrompt => {
            let prompt = state.prompt_plain_text();
            let action = if is_quit_prompt(&prompt) {
                InputAction::Exit
            } else {
                InputAction::Submit(prompt)
            };
            InputCommand::action_with_display(
                action,
                display(DisplayCommand::Prompt(PromptCommand::TakeForSubmit), true),
            )
        }
        KeyCommand::InsertPromptNewline => {
            InputCommand::display(DisplayCommand::Prompt(PromptCommand::Edit {
                operation: PromptEditCommand::InsertNewline,
                viewport: prompt_viewport,
                refresh_autocomplete: true,
            }))
        }
        KeyCommand::Exit => InputCommand::action(InputAction::Exit),
        KeyCommand::ClearInputOrExit => {
            if state.prompt_is_empty() {
                InputCommand::action(InputAction::Exit)
            } else {
                InputCommand::display(display(
                    DisplayCommand::Prompt(PromptCommand::Edit {
                        operation: PromptEditCommand::Clear,
                        viewport: prompt_viewport,
                        refresh_autocomplete: true,
                    }),
                    true,
                ))
            }
        }
        KeyCommand::FocusPrompt => InputCommand::display(display(
            DisplayCommand::Focus(FocusCommand::Prompt { close_help: true }),
            true,
        )),
        KeyCommand::FocusActivityTree => InputCommand::display(display(
            DisplayCommand::Focus(FocusCommand::ActivityTree),
            true,
        )),
        KeyCommand::CyclePrimaryAgent => {
            let before = state.selected_primary_agent_id().map(str::to_owned);
            let after = next_primary_agent_id(state);
            let command = DisplayCommand::batch([
                DisplayCommand::SetPrimaryAgent(after.clone()),
                DisplayCommand::RefreshAutocomplete,
            ]);
            if after != before {
                InputCommand::action_with_display(
                    InputAction::PrimaryAgentSelectionChanged(after),
                    command,
                )
            } else {
                InputCommand::display(command)
            }
        }
        KeyCommand::CycleThinkingLevel => {
            if let Some(next) = state.next_thinking_level() {
                InputCommand::action_with_display(
                    InputAction::ThinkingLevelSelectionChanged(next),
                    DisplayCommand::batch([
                        DisplayCommand::SetThinkingLevel(next),
                        DisplayCommand::RefreshAutocomplete,
                    ]),
                )
            } else {
                InputCommand::display(display(
                    DisplayCommand::SetStatus(
                        "thinking level is not configurable for this model".to_string(),
                    ),
                    true,
                ))
            }
        }
        KeyCommand::ToggleHelp => InputCommand::display(display(
            DisplayCommand::Focus(FocusCommand::ToggleHelp),
            true,
        )),
        KeyCommand::OpenModel => InputCommand::action(InputAction::OpenModel),
        KeyCommand::MoveSelectionUp if has_activity => InputCommand::display(display(
            DisplayCommand::Activity(ActivityCommand::MoveUp {
                visible_rows: activity_tree_viewport.visible_rows,
            }),
            true,
        )),
        KeyCommand::MoveSelectionDown if has_activity => InputCommand::display(display(
            DisplayCommand::Activity(ActivityCommand::MoveDown {
                visible_rows: activity_tree_viewport.visible_rows,
            }),
            true,
        )),
        KeyCommand::CollapseSelected if has_activity => InputCommand::display(display(
            DisplayCommand::Activity(ActivityCommand::CollapseSelected {
                visible_rows: activity_tree_viewport.visible_rows,
            }),
            true,
        )),
        KeyCommand::ExpandSelected if has_activity => InputCommand::display(display(
            DisplayCommand::Activity(ActivityCommand::ExpandSelected {
                visible_rows: activity_tree_viewport.visible_rows,
            }),
            true,
        )),
        KeyCommand::ExpandAll if has_activity => InputCommand::display(display(
            DisplayCommand::Activity(ActivityCommand::ExpandAll {
                visible_rows: activity_tree_viewport.visible_rows,
            }),
            true,
        )),
        KeyCommand::CollapseAll if has_activity => InputCommand::display(display(
            DisplayCommand::Activity(ActivityCommand::CollapseAll {
                visible_rows: activity_tree_viewport.visible_rows,
            }),
            true,
        )),
        KeyCommand::SelectFailed if has_activity => InputCommand::display(display(
            DisplayCommand::Activity(ActivityCommand::SelectStatus {
                status: crate::output::ActivityStatus::Failed,
                visible_rows: activity_tree_viewport.visible_rows,
            }),
            true,
        )),
        KeyCommand::SelectRunning if has_activity => InputCommand::display(display(
            DisplayCommand::Activity(ActivityCommand::SelectStatus {
                status: crate::output::ActivityStatus::Running,
                visible_rows: activity_tree_viewport.visible_rows,
            }),
            true,
        )),
        KeyCommand::ScrollDetailUp
        | KeyCommand::ScrollDetailDown
        | KeyCommand::PageDetailUp
        | KeyCommand::PageDetailDown => InputCommand::display(display(
            DisplayCommand::Scroll(ScrollCommand {
                target: ScrollTarget::Detail,
                down: matches!(
                    action,
                    KeyCommand::ScrollDetailDown | KeyCommand::PageDetailDown
                ),
                rows: if matches!(
                    action,
                    KeyCommand::PageDetailUp | KeyCommand::PageDetailDown
                ) {
                    detail_viewport.visible_rows
                } else {
                    1
                },
                visible_rows: detail_viewport.visible_rows,
                wrap_width: detail_viewport.wrap_width,
            }),
            true,
        )),
        KeyCommand::ScrollTranscriptUp => {
            InputCommand::display(DisplayCommand::Scroll(ScrollCommand {
                target: ScrollTarget::Transcript,
                down: false,
                rows: 1,
                visible_rows: transcript_viewport.visible_rows,
                wrap_width: transcript_viewport.wrap_width,
            }))
        }
        KeyCommand::ScrollTranscriptDown => {
            InputCommand::display(DisplayCommand::Scroll(ScrollCommand {
                target: ScrollTarget::Transcript,
                down: true,
                rows: 1,
                visible_rows: transcript_viewport.visible_rows,
                wrap_width: transcript_viewport.wrap_width,
            }))
        }
        KeyCommand::MovePromptCursorLeft => InputCommand::display(display(
            DisplayCommand::Prompt(PromptCommand::Edit {
                operation: PromptEditCommand::MoveCursorLeft,
                viewport: prompt_viewport,
                refresh_autocomplete: true,
            }),
            true,
        )),
        KeyCommand::MovePromptCursorRight => InputCommand::display(display(
            DisplayCommand::Prompt(PromptCommand::Edit {
                operation: PromptEditCommand::MoveCursorRight,
                viewport: prompt_viewport,
                refresh_autocomplete: true,
            }),
            true,
        )),
        KeyCommand::MovePromptCursorUp => InputCommand::display(display(
            DisplayCommand::Prompt(PromptCommand::Edit {
                operation: PromptEditCommand::MoveCursorUp,
                viewport: prompt_viewport,
                refresh_autocomplete: true,
            }),
            true,
        )),
        KeyCommand::MovePromptCursorDown => InputCommand::display(display(
            DisplayCommand::Prompt(PromptCommand::Edit {
                operation: PromptEditCommand::MoveCursorDown,
                viewport: prompt_viewport,
                refresh_autocomplete: true,
            }),
            true,
        )),
        KeyCommand::UndoPrompt => prompt_edit_command(PromptEditCommand::Undo, prompt_viewport),
        KeyCommand::RedoPrompt => prompt_edit_command(PromptEditCommand::Redo, prompt_viewport),
        KeyCommand::MovePromptWordLeft => {
            prompt_edit_command(PromptEditCommand::MoveWordLeft, prompt_viewport)
        }
        KeyCommand::MovePromptWordRight => {
            prompt_edit_command(PromptEditCommand::MoveWordRight, prompt_viewport)
        }
        KeyCommand::DeletePromptWord => {
            prompt_edit_command(PromptEditCommand::DeleteWord, prompt_viewport)
        }
        KeyCommand::DeletePromptNextWord => {
            prompt_edit_command(PromptEditCommand::DeleteNextWord, prompt_viewport)
        }
        KeyCommand::ExtendPromptSelectionLeft => {
            prompt_edit_command(PromptEditCommand::ExtendSelectionLeft, prompt_viewport)
        }
        KeyCommand::ExtendPromptSelectionRight => {
            prompt_edit_command(PromptEditCommand::ExtendSelectionRight, prompt_viewport)
        }
        KeyCommand::ExtendPromptSelectionUp => {
            prompt_edit_command(PromptEditCommand::ExtendSelectionUp, prompt_viewport)
        }
        KeyCommand::ExtendPromptSelectionDown => {
            prompt_edit_command(PromptEditCommand::ExtendSelectionDown, prompt_viewport)
        }
        KeyCommand::SelectAllPrompt => {
            prompt_edit_command(PromptEditCommand::SelectAll, prompt_viewport)
        }
        KeyCommand::MoveSelectionUp
        | KeyCommand::MoveSelectionDown
        | KeyCommand::CollapseSelected
        | KeyCommand::ExpandSelected
        | KeyCommand::ExpandAll
        | KeyCommand::CollapseAll
        | KeyCommand::SelectFailed
        | KeyCommand::SelectRunning => InputCommand::display(DisplayCommand::RefreshAutocomplete),
    }
}

fn prompt_edit_command(operation: PromptEditCommand, viewport: PromptViewport) -> InputCommand {
    InputCommand::display(DisplayCommand::Prompt(PromptCommand::Edit {
        operation,
        viewport,
        refresh_autocomplete: true,
    }))
}