magi-code 0.62.0

Repository-aware CLI coding agent for terminal work
Documentation
pub(crate) mod diff;
pub(crate) mod highlight;
pub(crate) mod markup;

#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) enum RenderInputKind {
    Markdown,
    Plain,
    Code { language: Option<String> },
    Diff,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct DisplayLine {
    pub(crate) spans: Vec<DisplaySpan>,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct DisplaySpan {
    pub(crate) text: String,
    pub(crate) role: DisplayRole,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum DisplayRole {
    Plain,
    Heading,
    ListMarker,
    BlockQuote,
    InlineCode,
    CodeFence,
    CodeLanguageLabel,
    CodeBlockGutter,
    Keyword,
    String,
    Comment,
    Number,
    Function,
    Type,
    Operator,
    Punctuation,
    FallbackCode,
    DiffInserted,
    DiffRemoved,
    DiffChanged,
    DiffContext,
    DiffHunkHeader,
    DiffFileHeader,
    DiffMetadata,
}

impl DisplayLine {
    pub(crate) fn plain<T: Into<String>>(text: T) -> Self {
        Self {
            spans: vec![DisplaySpan::new(text, DisplayRole::Plain)],
        }
    }

    pub(crate) fn from_span<T: Into<String>>(text: T, role: DisplayRole) -> Self {
        Self {
            spans: vec![DisplaySpan::new(text, role)],
        }
    }

    pub(crate) fn plain_text(&self) -> String {
        self.spans.iter().map(|span| span.text.as_str()).collect()
    }

    pub(crate) fn is_blank(&self) -> bool {
        self.spans.iter().all(|span| span.text.is_empty())
    }
}

impl DisplaySpan {
    pub(crate) fn new<T: Into<String>>(text: T, role: DisplayRole) -> Self {
        Self {
            text: text.into(),
            role,
        }
    }
}

pub(crate) fn render(input: &str, kind: RenderInputKind) -> Vec<DisplayLine> {
    match kind {
        RenderInputKind::Markdown => markup::render_markdown(input),
        RenderInputKind::Plain => render_plain(input),
        RenderInputKind::Code { language } => highlight::highlight_code(input, language.as_deref()),
        RenderInputKind::Diff => diff::render_diff(input),
    }
}

pub(crate) fn render_plain(input: &str) -> Vec<DisplayLine> {
    if input.is_empty() {
        return vec![DisplayLine::plain("")];
    }
    input.split('\n').map(DisplayLine::plain).collect()
}

pub(crate) fn plain_projection(lines: &[DisplayLine]) -> String {
    lines
        .iter()
        .map(DisplayLine::plain_text)
        .collect::<Vec<_>>()
        .join("\n")
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn plain_projection_round_trips_visible_text() {
        let lines = render("a\nb", RenderInputKind::Plain);
        assert_eq!(plain_projection(&lines), "a\nb");
    }

    #[test]
    fn is_blank_detects_empty_and_nonempty() {
        assert!(DisplayLine::plain("").is_blank());
        assert!(!DisplayLine::plain("text").is_blank());
    }

    #[test]
    fn empty_input_is_one_empty_line() {
        assert_eq!(
            render("", RenderInputKind::Plain),
            vec![DisplayLine::plain("")]
        );
    }
}