magi-code 0.63.4

Repository-aware CLI coding agent for terminal work
Documentation
#[cfg(test)]
use super::CARD_LAYOUT_MIN_WIDTH;
use super::visual_lines::{TranscriptVisualLine, visual_line_rows, visual_lines};
use crate::{
    output::ActivityId,
    tui::{state::MissionControlState, transcript},
};

fn copy_position_for_visual_cell(
    line: &TranscriptVisualLine,
    row: usize,
    column: usize,
    wrap_width: u16,
) -> Option<crate::tui::input::selection::TextPosition> {
    let column_offset = usize::from(line.visual_copy_column_offset);
    let aligned_copy_text = if column_offset == 0 {
        line.copy_text.clone()
    } else {
        format!("{}{}", " ".repeat(column_offset), line.copy_text)
    };
    let position =
        transcript::text_position_for_visual_cell(&aligned_copy_text, row, column, wrap_width)?;
    Some(crate::tui::input::selection::TextPosition::new(
        position
            .byte
            .saturating_sub(column_offset)
            .min(line.copy_text.len()),
        row,
        column,
    ))
}

pub(crate) fn text_position_for_visual_cell(
    state: &MissionControlState,
    visual_row: usize,
    column: usize,
    wrap_width: u16,
) -> Option<crate::tui::input::selection::TextPosition> {
    let lines = visual_lines(state, wrap_width);
    let mut current_row = 0usize;
    let mut byte_base = 0usize;
    let mut copy_line_seen = false;
    for line in &lines {
        if line.copyable && !line.copy_continuation && copy_line_seen {
            byte_base = byte_base.saturating_add(1);
        }
        if line.copyable {
            copy_line_seen = true;
        }

        let rows = visual_line_rows(line, wrap_width);
        let next_row = current_row.saturating_add(rows);
        if visual_row < next_row {
            if !line.copyable {
                return None;
            }
            let row_in_line = visual_row.saturating_sub(current_row);
            let position = copy_position_for_visual_cell(line, row_in_line, column, wrap_width)?;
            return Some(crate::tui::input::selection::TextPosition::new(
                byte_base.saturating_add(position.byte),
                visual_row,
                column,
            ));
        }
        current_row = next_row;
        if line.copyable {
            byte_base = byte_base.saturating_add(line.copy_text.len());
        }
    }
    None
}

pub(crate) fn activity_button_for_visual_cell(
    state: &MissionControlState,
    visual_row: usize,
    column: usize,
    wrap_width: u16,
) -> Option<ActivityId> {
    let lines = visual_lines(state, wrap_width);
    let mut current_row = 0usize;
    for line in &lines {
        let rows = visual_line_rows(line, wrap_width);
        let next_row = current_row.saturating_add(rows);
        if visual_row < next_row {
            let button = line.activity_button.as_ref()?;
            let row_in_line = visual_row.saturating_sub(current_row);
            return (row_in_line == 0
                && column >= usize::from(button.start_column)
                && column < usize::from(button.end_column))
            .then(|| button.activity_id.clone());
        }
        current_row = next_row;
    }
    None
}

#[cfg(test)]
pub(crate) fn visual_text(state: &MissionControlState) -> String {
    visual_text_for_width(state, CARD_LAYOUT_MIN_WIDTH)
}

pub(crate) fn visual_text_for_width(state: &MissionControlState, wrap_width: u16) -> String {
    let mut text = String::new();
    let mut copy_line_seen = false;
    for line in visual_lines(state, wrap_width)
        .into_iter()
        .filter(|line| line.copyable)
    {
        if !line.copy_continuation {
            if copy_line_seen {
                text.push('\n');
            }
            copy_line_seen = true;
        }
        text.push_str(&line.copy_text);
    }
    text
}