magi-code 0.64.1

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

impl MissionControlState {
    pub(crate) fn prompt_text(&self) -> String {
        prompt::prompt_text(
            &self.input,
            self.is_prompt_focused(),
            self.prompt_cursor,
            self.prompt_cursor_visible,
        )
    }

    pub(crate) fn prompt_text_position_for_cell(
        &self,
        row: u16,
        column: u16,
        visible_rows: u16,
        wrap_width: u16,
    ) -> TextPosition {
        let visual_row = usize::from(self.prompt_render_scroll(visible_rows, wrap_width))
            .saturating_add(usize::from(row));
        let byte = prompt::prompt_byte_index_for_visual_position_with_cursor(
            &self.input,
            visual_row,
            usize::from(column),
            wrap_width,
            (self.is_prompt_focused() && self.prompt_cursor_visible).then_some(self.prompt_cursor),
        );
        TextPosition::new(byte, visual_row, usize::from(column))
    }

    pub(crate) fn move_prompt_cursor_to(
        &mut self,
        byte: usize,
        visible_rows: u16,
        wrap_width: u16,
    ) {
        self.prompt_cursor = prompt::clamped_byte_index(&self.input, byte);
        self.prompt_target_column = None;
        self.focus_prompt();
        let mut scroll = self.prompt_offset();
        prompt::ensure_cursor_visible(
            &self.input,
            self.prompt_cursor,
            &mut scroll,
            visible_rows,
            wrap_width,
        );
        self.set_scroll_offset(&self.scroll_views.prompt, scroll);
    }

    fn with_prompt_edit<R>(&mut self, f: impl FnOnce(&mut prompt::PromptEdit<'_>) -> R) -> R {
        let mut scroll = self.prompt_offset();
        let result = {
            let mut edit = prompt::PromptEdit {
                input: &mut self.input,
                cursor: &mut self.prompt_cursor,
                target_column: &mut self.prompt_target_column,
                scroll: &mut scroll,
                cursor_visible: &mut self.prompt_cursor_visible,
            };
            f(&mut edit)
        };
        self.set_scroll_offset(&self.scroll_views.prompt, scroll);
        result
    }

    pub(crate) fn move_prompt_cursor_left(&mut self, visible_rows: u16, wrap_width: u16) {
        let mut scroll = self.prompt_offset();
        prompt::move_cursor_left(
            &self.input,
            &mut self.prompt_cursor,
            &mut self.prompt_target_column,
            &mut scroll,
            &mut self.prompt_cursor_visible,
            visible_rows,
            wrap_width,
        );
        self.set_scroll_offset(&self.scroll_views.prompt, scroll);
    }

    pub(crate) fn move_prompt_cursor_right(&mut self, visible_rows: u16, wrap_width: u16) {
        let mut scroll = self.prompt_offset();
        prompt::move_cursor_right(
            &self.input,
            &mut self.prompt_cursor,
            &mut self.prompt_target_column,
            &mut scroll,
            &mut self.prompt_cursor_visible,
            visible_rows,
            wrap_width,
        );
        self.set_scroll_offset(&self.scroll_views.prompt, scroll);
    }

    pub(crate) fn move_prompt_cursor_up(&mut self, visible_rows: u16, wrap_width: u16) {
        self.with_prompt_edit(|edit| {
            prompt::move_cursor_vertical(edit, -1, visible_rows, wrap_width)
        });
    }

    pub(crate) fn move_prompt_cursor_down(&mut self, visible_rows: u16, wrap_width: u16) {
        self.with_prompt_edit(|edit| {
            prompt::move_cursor_vertical(edit, 1, visible_rows, wrap_width)
        });
    }

    pub(crate) fn insert_prompt_char(&mut self, ch: char, visible_rows: u16, wrap_width: u16) {
        self.with_prompt_edit(|edit| prompt::insert_char(edit, ch, visible_rows, wrap_width));
    }

    pub(crate) fn insert_prompt_str(&mut self, text: &str, visible_rows: u16, wrap_width: u16) {
        self.with_prompt_edit(|edit| prompt::insert_str(edit, text, visible_rows, wrap_width));
    }

    pub(crate) fn backspace_prompt_char(&mut self, visible_rows: u16, wrap_width: u16) {
        let mut scroll = self.prompt_offset();
        prompt::backspace_char(
            &mut self.input,
            &mut self.prompt_cursor,
            &mut self.prompt_target_column,
            &mut scroll,
            &mut self.prompt_cursor_visible,
            visible_rows,
            wrap_width,
        );
        self.set_scroll_offset(&self.scroll_views.prompt, scroll);
    }

    pub(crate) fn delete_prompt_char(&mut self, visible_rows: u16, wrap_width: u16) {
        let mut scroll = self.prompt_offset();
        prompt::delete_char(
            &mut self.input,
            &mut self.prompt_cursor,
            &mut self.prompt_target_column,
            &mut scroll,
            &mut self.prompt_cursor_visible,
            visible_rows,
            wrap_width,
        );
        self.set_scroll_offset(&self.scroll_views.prompt, scroll);
    }

    pub(crate) fn clear_prompt_input(&mut self) {
        let mut scroll = self.prompt_offset();
        prompt::reset_input(
            &mut self.input,
            &mut self.prompt_cursor,
            &mut scroll,
            &mut self.prompt_target_column,
            &mut self.prompt_cursor_visible,
        );
        self.set_scroll_offset(&self.scroll_views.prompt, scroll);
        self.clear_autocomplete();
    }

    pub(crate) fn take_prompt_for_submit(&mut self) -> String {
        let mut scroll = self.prompt_offset();
        let submitted = prompt::take_for_submit(
            &mut self.input,
            &mut self.prompt_cursor,
            &mut scroll,
            &mut self.prompt_target_column,
            &mut self.prompt_cursor_visible,
        );
        self.set_scroll_offset(&self.scroll_views.prompt, scroll);
        self.clear_autocomplete();
        submitted
    }

    pub(crate) fn autocomplete_visible(&self) -> bool {
        autocomplete::visible(&self.autocomplete)
    }

    pub(crate) fn clear_autocomplete(&mut self) {
        autocomplete::clear(
            &mut self.autocomplete,
            &mut self.autocomplete_dismissed_token,
        );
    }

    pub(crate) fn reset_for_new_session(&mut self) {
        self.nodes.clear();
        self.roots.clear();
        self.children.clear();
        self.expanded.clear();
        self.transcript.clear();
        self.transcript_activity_links.clear();
        self.active_tool_transcripts.clear();
        self.running_prompt = None;
        self.assistant_streaming = false;
        self.active_assistant_text.clear();
        self.active_assistant_redactor = StreamingRedactor::default();
        self.active_assistant_transcript_index = None;
        self.active_thinking_text.clear();
        self.active_thinking_redactor = StreamingRedactor::default();
        self.active_thinking_control_sanitizer.reset();
        self.active_thinking_transcript_index = None;
        self.context_usage = None;
        self.pending_steering_count = 0;
        self.selected = 0;
        self.reset_core_scroll_views();
        self.activity_follow_tail = false;
        self.modals.system_prompt_modal = None;
        self.modals.skills_modal = None;
        self.modals.session_picker = None;
        self.selection = None;
        self.toast = None;
        self.clear_autocomplete();
        self.invalidate_transcript_cache();
        self.invalidate_activity_render_cache();
    }

    pub(crate) fn hide_autocomplete(&mut self) {
        autocomplete::hide(&mut self.autocomplete);
    }

    pub(crate) fn recompute_autocomplete(&mut self, candidates: &[AutocompleteCandidate]) {
        autocomplete::recompute(
            &self.input,
            self.prompt_cursor,
            &mut self.autocomplete,
            &self.autocomplete_dismissed_token,
            candidates,
        );
    }

    pub(crate) fn dismiss_autocomplete_for_current_token(&mut self) {
        autocomplete::dismiss_for_current_token(
            &mut self.autocomplete,
            &mut self.autocomplete_dismissed_token,
        );
    }

    pub(crate) fn move_autocomplete_selection_down(&mut self) {
        autocomplete::move_selection_down(&mut self.autocomplete);
    }
    pub(crate) fn move_autocomplete_selection_up(&mut self) {
        autocomplete::move_selection_up(&mut self.autocomplete);
    }

    pub(crate) fn accept_autocomplete(&mut self, visible_rows: u16, wrap_width: u16) -> bool {
        let mut autocomplete = self.autocomplete.take();
        let mut dismissed = self.autocomplete_dismissed_token.take();
        let accepted = self.with_prompt_edit(|edit| {
            autocomplete::accept(
                edit,
                &mut autocomplete,
                &mut dismissed,
                visible_rows,
                wrap_width,
            )
        });
        self.autocomplete = autocomplete;
        self.autocomplete_dismissed_token = dismissed;
        accepted
    }
    pub(crate) fn scroll_prompt_up_by(
        &mut self,
        rows: u16,
        visible_rows: u16,
        wrap_width: u16,
    ) -> bool {
        let prompt_text = self.prompt_text();
        let before = self.prompt_offset();
        let mut scroll = before;
        let changed =
            prompt::scroll_up_by(&prompt_text, &mut scroll, rows, visible_rows, wrap_width);
        self.set_scroll_offset(&self.scroll_views.prompt, scroll);
        changed && self.prompt_offset() != before
    }

    pub(crate) fn scroll_prompt_down_by(
        &mut self,
        rows: u16,
        visible_rows: u16,
        wrap_width: u16,
    ) -> bool {
        let prompt_text = self.prompt_text();
        let before = self.prompt_offset();
        let mut scroll = before;
        let changed =
            prompt::scroll_down_by(&prompt_text, &mut scroll, rows, visible_rows, wrap_width);
        self.set_scroll_offset(&self.scroll_views.prompt, scroll);
        changed && self.prompt_offset() != before
    }
    #[cfg(test)]
    pub(crate) fn ensure_prompt_cursor_visible(&mut self, visible_rows: u16, wrap_width: u16) {
        let mut scroll = self.prompt_offset();
        prompt::ensure_cursor_visible(
            &self.input,
            self.prompt_cursor,
            &mut scroll,
            visible_rows,
            wrap_width,
        );
        self.set_scroll_offset(&self.scroll_views.prompt, scroll);
    }

    pub(crate) fn prompt_visible_rows(&self, wrap_width: u16) -> u16 {
        let prompt_text = self.prompt_text();
        prompt::visible_rows(&prompt_text, wrap_width)
    }

    pub(crate) fn prompt_render_scroll(&self, _visible_rows: u16, _wrap_width: u16) -> u16 {
        self.prompt_offset().min(u16::MAX as usize) as u16
    }

    #[cfg(test)]
    pub(crate) fn prompt_visual_rows(&self, wrap_width: u16) -> usize {
        let prompt_text = self.prompt_text();
        prompt::visual_rows(&prompt_text, wrap_width)
    }
}