rho-coding-agent 1.48.0

A lightweight agent harness inspired by Pi
use std::fmt::{self, Debug};

use crate::tui::markdown::txm::ParseError;

#[derive(Default, Clone, Copy, PartialEq, Eq)]
pub struct Style(u32);

impl Debug for Style {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut fmt = f.debug_tuple("Style");

        if self.is_bold() {
            fmt.field(&"bold");
        }
        if self.is_italic() {
            fmt.field(&"italic");
        }
        if self.is_underline() {
            fmt.field(&"underline");
        }
        if self.is_dim() {
            fmt.field(&"dim");
        }
        if self.fg_color() != 0 {
            fmt.field(&self.fg_color());
        }
        if self.bg_color() != 0 {
            fmt.field(&self.bg_color());
        }

        fmt.finish()
    }
}

const BOLD: u32 = 1 << 0;
const ITALIC: u32 = 1 << 1;
const UNDERLINE: u32 = 1 << 2;
const DIM: u32 = 1 << 3;

const FG_MASK: u32 = 0x000FF0;
const BG_MASK: u32 = 0x0FF000;

const LOCK_BOLD: u32 = 1 << 20;
const LOCK_ITALIC: u32 = 1 << 21;
const LOCK_UNDERLINE: u32 = 1 << 22;
const LOCK_DIM: u32 = 1 << 23;
const LOCK_FG: u32 = 1 << 24;
const LOCK_BG: u32 = 1 << 25;

const ATTR_MASK: u32 = BOLD | ITALIC | UNDERLINE | DIM;

impl Style {
    pub const fn new() -> Self {
        Style(0)
    }

    pub const fn bold(mut self) -> Self {
        self.0 |= BOLD | LOCK_BOLD;
        self
    }

    pub const fn italic(mut self) -> Self {
        self.0 |= ITALIC | LOCK_ITALIC;
        self
    }

    pub const fn underline(mut self) -> Self {
        self.0 |= UNDERLINE | LOCK_UNDERLINE;
        self
    }

    pub const fn dim(mut self) -> Self {
        self.0 |= DIM | LOCK_DIM;
        self
    }

    pub const fn un_bold(mut self) -> Self {
        self.0 = (self.0 & !BOLD) | LOCK_BOLD;
        self
    }

    pub const fn un_italic(mut self) -> Self {
        self.0 = (self.0 & !ITALIC) | LOCK_ITALIC;
        self
    }

    pub const fn un_underline(mut self) -> Self {
        self.0 = (self.0 & !UNDERLINE) | LOCK_UNDERLINE;
        self
    }

    pub const fn un_dim(mut self) -> Self {
        self.0 = (self.0 & !DIM) | LOCK_DIM;
        self
    }

    pub const fn fg(mut self, color: Color) -> Self {
        self.0 = (self.0 & !FG_MASK) | ((color.0 as u32) << 4) | LOCK_FG;
        self
    }

    pub const fn bg(mut self, color: Color) -> Self {
        self.0 = (self.0 & !BG_MASK) | ((color.0 as u32) << 12) | LOCK_BG;
        self
    }

    pub const fn is_bold(self) -> bool {
        self.0 & BOLD != 0
    }

    pub const fn is_italic(self) -> bool {
        self.0 & ITALIC != 0
    }

    pub const fn is_underline(self) -> bool {
        self.0 & UNDERLINE != 0
    }

    pub const fn is_dim(self) -> bool {
        self.0 & DIM != 0
    }

    pub const fn fg_color(self) -> u8 {
        ((self.0 & FG_MASK) >> 4) as u8
    }

    pub const fn bg_color(self) -> u8 {
        ((self.0 & BG_MASK) >> 12) as u8
    }

    pub const fn merge(self, other: Style) -> Style {
        let combined_lock = self.0 | other.0;

        let bold = if other.0 & LOCK_BOLD != 0 {
            other.0 & BOLD
        } else if self.0 & LOCK_BOLD != 0 {
            self.0 & BOLD
        } else {
            (self.0 | other.0) & BOLD
        };

        let italic = if other.0 & LOCK_ITALIC != 0 {
            other.0 & ITALIC
        } else if self.0 & LOCK_ITALIC != 0 {
            self.0 & ITALIC
        } else {
            (self.0 | other.0) & ITALIC
        };

        let underline = if other.0 & LOCK_UNDERLINE != 0 {
            other.0 & UNDERLINE
        } else if self.0 & LOCK_UNDERLINE != 0 {
            self.0 & UNDERLINE
        } else {
            (self.0 | other.0) & UNDERLINE
        };

        let dim = if other.0 & LOCK_DIM != 0 {
            other.0 & DIM
        } else if self.0 & LOCK_DIM != 0 {
            self.0 & DIM
        } else {
            (self.0 | other.0) & DIM
        };

        let fg = if other.0 & LOCK_FG != 0 {
            other.0 & FG_MASK
        } else if self.0 & LOCK_FG != 0 {
            self.0 & FG_MASK
        } else {
            let other_fg = other.0 & FG_MASK;
            if other_fg != 0 {
                other_fg
            } else {
                self.0 & FG_MASK
            }
        };

        let bg = if other.0 & LOCK_BG != 0 {
            other.0 & BG_MASK
        } else if self.0 & LOCK_BG != 0 {
            self.0 & BG_MASK
        } else {
            let other_bg = other.0 & BG_MASK;
            if other_bg != 0 {
                other_bg
            } else {
                self.0 & BG_MASK
            }
        };

        let lock_part = combined_lock
            & (LOCK_BOLD | LOCK_ITALIC | LOCK_UNDERLINE | LOCK_DIM | LOCK_FG | LOCK_BG);
        Style(lock_part | bold | italic | underline | dim | fg | bg)
    }

    pub const fn is_empty(self) -> bool {
        self.0 & (ATTR_MASK | FG_MASK | BG_MASK) == 0
    }

    pub fn write_ansi_prefix(&self, f: &mut impl fmt::Write) -> fmt::Result {
        if self.is_empty() {
            return Ok(());
        }

        write!(f, "\x1b[")?;
        let mut first = true;

        if self.is_bold() {
            write!(f, "1")?;
            first = false;
        }
        if self.is_italic() {
            write!(f, "{}3", if first { "" } else { ";" })?;
            first = false;
        }
        if self.is_underline() {
            write!(f, "{}4", if first { "" } else { ";" })?;
            first = false;
        }
        if self.is_dim() {
            write!(f, "{}2", if first { "" } else { ";" })?;
            first = false;
        }

        let fg = self.fg_color();
        let bg = self.bg_color();

        if fg != 0 {
            write!(f, "{}38;5;{}", if first { "" } else { ";" }, fg)?;
            first = false;
        }

        if bg != 0 {
            write!(f, "{}48;5;{}", if first { "" } else { ";" }, bg)?;
        }

        write!(f, "m")?;
        Ok(())
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Color(u8);

impl Color {
    pub const NONE: Color = Color(0);
    pub const BLACK: Color = Color(1);
    pub const RED: Color = Color(2);
    pub const GREEN: Color = Color(3);
    pub const YELLOW: Color = Color(4);
    pub const BLUE: Color = Color(5);
    pub const MAGENTA: Color = Color(6);
    pub const CYAN: Color = Color(7);
    pub const WHITE: Color = Color(8);
    pub const BRIGHT_BLACK: Color = Color(9);
    pub const BRIGHT_RED: Color = Color(10);
    pub const BRIGHT_GREEN: Color = Color(11);
    pub const BRIGHT_YELLOW: Color = Color(12);
    pub const BRIGHT_BLUE: Color = Color(13);
    pub const BRIGHT_MAGENTA: Color = Color(14);
    pub const BRIGHT_CYAN: Color = Color(15);
    pub const BRIGHT_WHITE: Color = Color(16);

    pub const fn new(n: u8) -> Self {
        Color(n)
    }

    pub const fn value(self) -> u8 {
        self.0
    }
}

pub fn parse_color(s: &str) -> Result<Color, ParseError> {
    Ok(match s {
        "black" => Color::BLACK,
        "red" => Color::RED,
        "green" => Color::GREEN,
        "yellow" => Color::YELLOW,
        "blue" => Color::BLUE,
        "magenta" => Color::MAGENTA,
        "cyan" => Color::CYAN,
        "white" => Color::WHITE,
        "brightblack" | "gray" | "grey" => Color::BRIGHT_BLACK,
        "brightred" => Color::BRIGHT_RED,
        "brightgreen" => Color::BRIGHT_GREEN,
        "brightyellow" => Color::BRIGHT_YELLOW,
        "brightblue" => Color::BRIGHT_BLUE,
        "brightmagenta" => Color::BRIGHT_MAGENTA,
        "brightcyan" => Color::BRIGHT_CYAN,
        "brightwhite" => Color::BRIGHT_WHITE,

        _ => {
            return Err(ParseError::InvalidColor {
                name: s.to_string(),
            });
        }
    })
}