Files
Kigi-CLI/crates/codegen/kigi-markdown/src/style.rs
T
ZacharyZhang-NY a02b555e66 docs(comments): rewrite comments across all crates to the guidelines
Sweep every first-party crate source (1956 .rs files) to the project comment
guidelines: delete redundant restatements, decorative banners, change
narration, and end-of-line comments; keep and tighten the crucial ones
(invariants, bug rationale, SAFETY blocks, ported-source attribution).

No functional code changed. Every edit is proven comment-only against the
prior tree by a comment-stripping lexer (string/char/raw-string aware) plus a
separate doctest-fence check. Where removing a comment made rustfmt or clippy
want to re-lay-out adjacent code, the minimal triggering comment is restored so
code tokens stay byte-identical.

Gates green: cargo fmt --all --check (0 diffs), cargo check and cargo clippy
--workspace --all-targets (0 warnings).

Adds scripts/check_codegen_comment_guidelines.py — the enforcement gate for
these guidelines (flags banners, end-of-line comments, change narration, and
commented-out code).
2026-07-23 16:55:39 -04:00

301 lines
9.8 KiB
Rust

//! Markdown styling types.
use anstyle::{Effects, Style};
use crate::colors::adapt_style;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct TableBorders {
chars: [char; 11],
}
impl TableBorders {
const H: usize = 0;
const V: usize = 1;
const TL: usize = 2;
const TR: usize = 3;
const BL: usize = 4;
const BR: usize = 5;
const T_T: usize = 6;
const T_B: usize = 7;
const T_L: usize = 8;
const T_R: usize = 9;
const X: usize = 10;
pub const BOX: Self = Self {
chars: ['─', '│', '┌', '┐', '└', '┘', '┬', '┴', '├', '┤', '┼'],
};
pub const ASCII: Self = Self {
chars: ['-', '|', '+', '+', '+', '+', '+', '+', '+', '+', '+'],
};
pub const DOUBLE: Self = Self {
chars: ['═', '║', '╔', '╗', '╚', '╝', '╦', '╩', '╠', '╣', '╬'],
};
pub const fn new(chars: [char; 11]) -> Self {
Self { chars }
}
// Every character is reachable under two names: a terse one for dense
// table-formatting expressions and a spelled-out one for everything else.
pub const fn h(&self) -> char {
self.chars[Self::H]
}
pub const fn v(&self) -> char {
self.chars[Self::V]
}
pub const fn c_tl(&self) -> char {
self.chars[Self::TL]
}
pub const fn c_tr(&self) -> char {
self.chars[Self::TR]
}
pub const fn c_bl(&self) -> char {
self.chars[Self::BL]
}
pub const fn c_br(&self) -> char {
self.chars[Self::BR]
}
pub const fn t_t(&self) -> char {
self.chars[Self::T_T]
}
pub const fn t_b(&self) -> char {
self.chars[Self::T_B]
}
pub const fn t_l(&self) -> char {
self.chars[Self::T_L]
}
pub const fn t_r(&self) -> char {
self.chars[Self::T_R]
}
pub const fn x(&self) -> char {
self.chars[Self::X]
}
pub const fn horizontal(&self) -> char {
self.chars[Self::H]
}
pub const fn vertical(&self) -> char {
self.chars[Self::V]
}
pub const fn top_left(&self) -> char {
self.chars[Self::TL]
}
pub const fn top_right(&self) -> char {
self.chars[Self::TR]
}
pub const fn bottom_left(&self) -> char {
self.chars[Self::BL]
}
pub const fn bottom_right(&self) -> char {
self.chars[Self::BR]
}
pub const fn t_top(&self) -> char {
self.chars[Self::T_T]
}
pub const fn t_bottom(&self) -> char {
self.chars[Self::T_B]
}
pub const fn t_left(&self) -> char {
self.chars[Self::T_L]
}
pub const fn t_right(&self) -> char {
self.chars[Self::T_R]
}
pub const fn cross(&self) -> char {
self.chars[Self::X]
}
}
impl Default for TableBorders {
fn default() -> Self {
Self::BOX
}
}
/// Style configuration for markdown rendering.
///
/// `_inner` variants style an element's content; `_outer` variants style its
/// syntax markers, which pretty mode hides.
#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, Hash)]
pub struct MarkdownStyle {
pub heading_inner: [Style; 6],
pub heading_outer: [Style; 6],
pub strong_inner: Style,
pub strong_outer: Style,
pub emphasis_inner: Style,
pub emphasis_outer: Style,
pub strikethrough_inner: Style,
pub strikethrough_outer: Style,
pub inline_code_inner: Style,
pub inline_code_outer: Style,
pub blockquote_outer: Style,
pub task_checked: Style,
pub task_unchecked: Style,
pub list_item: Style,
pub rule: Style,
pub link_outer: Style,
pub link_text: Style,
pub link_url: Style,
pub link_title: Style,
pub code_outer: Style,
pub code_language: Style,
pub code_untagged: Style,
pub code_background: Style,
pub table_outer: Style,
/// Default foreground for plain body text (paragraphs with no formatting).
/// When set, the renderer applies this to text spans that would otherwise
/// inherit the terminal's default foreground.
pub text: Style,
/// Style for rendered LaTeX math (inline `$...$`/`\(...\)` content after
/// Unicode conversion, and display math block lines). In raw mode the
/// style applies to the unconverted TeX source.
pub math: Style,
}
impl MarkdownStyle {
/// Downgrade every style's RGB colors to 256-color or 16-color to match
/// the terminal's capabilities.
pub fn adapt(self) -> Self {
Self {
heading_inner: [
adapt_style(self.heading_inner[0]),
adapt_style(self.heading_inner[1]),
adapt_style(self.heading_inner[2]),
adapt_style(self.heading_inner[3]),
adapt_style(self.heading_inner[4]),
adapt_style(self.heading_inner[5]),
],
heading_outer: [
adapt_style(self.heading_outer[0]),
adapt_style(self.heading_outer[1]),
adapt_style(self.heading_outer[2]),
adapt_style(self.heading_outer[3]),
adapt_style(self.heading_outer[4]),
adapt_style(self.heading_outer[5]),
],
strong_inner: adapt_style(self.strong_inner),
strong_outer: adapt_style(self.strong_outer),
emphasis_inner: adapt_style(self.emphasis_inner),
emphasis_outer: adapt_style(self.emphasis_outer),
strikethrough_inner: adapt_style(self.strikethrough_inner),
strikethrough_outer: adapt_style(self.strikethrough_outer),
inline_code_inner: adapt_style(self.inline_code_inner),
inline_code_outer: adapt_style(self.inline_code_outer),
blockquote_outer: adapt_style(self.blockquote_outer),
task_checked: adapt_style(self.task_checked),
task_unchecked: adapt_style(self.task_unchecked),
list_item: adapt_style(self.list_item),
rule: adapt_style(self.rule),
link_outer: adapt_style(self.link_outer),
link_text: adapt_style(self.link_text),
link_url: adapt_style(self.link_url),
link_title: adapt_style(self.link_title),
code_outer: adapt_style(self.code_outer),
code_language: adapt_style(self.code_language),
code_untagged: adapt_style(self.code_untagged),
code_background: adapt_style(self.code_background),
table_outer: adapt_style(self.table_outer),
text: adapt_style(self.text),
math: adapt_style(self.math),
}
}
}
/// Pretty mode skips a span when every style covering it is HIDDEN. An empty
/// style set is not hidden.
pub(crate) fn all_hidden(styles: impl IntoIterator<Item = Option<Style>>) -> bool {
let mut has_any = false;
let mut all_are_hidden = true;
for style in styles {
has_any = true;
match style {
Some(s) if s.get_effects().contains(Effects::HIDDEN) => {}
_ => {
all_are_hidden = false;
}
}
}
has_any && all_are_hidden
}
/// Merge multiple styles into one for rendering.
///
/// HIDDEN is a semantic marker rather than a visual effect, so it never
/// reaches the output: a style that raised it contributes nothing at all, and
/// a trailing HIDDEN is dropped from the result.
pub(crate) fn merge_styles(styles: impl IntoIterator<Item = Option<Style>>) -> Style {
let mut out = Style::new();
let mut prev = Style::new();
for style in styles {
// Rewind past a style that raised HIDDEN before folding in the next one.
if out.get_effects().contains(Effects::HIDDEN) {
out = prev;
} else {
prev = out;
}
let Some(style) = style else {
continue;
};
if !style.get_effects().is_plain() {
out = out.effects(out.get_effects() | style.get_effects());
}
if style.get_effects().contains(Effects::DIMMED) {
out = out.effects(out.get_effects().remove(Effects::BOLD));
}
if style.get_effects().contains(Effects::BOLD) {
out = out.effects(out.get_effects().remove(Effects::DIMMED));
}
if let Some(color) = style.get_fg_color() {
out = out.fg_color(Some(color));
}
if let Some(color) = style.get_bg_color() {
out = out.bg_color(Some(color));
}
if let Some(color) = style.get_underline_color() {
out = out.underline_color(Some(color));
}
}
out.effects(out.get_effects().remove(Effects::HIDDEN))
}
#[cfg(any(test, fuzzing))]
pub mod test_style {
use super::MarkdownStyle;
use anstyle::Style;
/// Effects only, no colors, so assertions compare stable escape sequences.
pub const STYLE: MarkdownStyle = MarkdownStyle {
heading_inner: [Style::new().bold(); 6],
heading_outer: [Style::new().dimmed().hidden(); 6],
strong_inner: Style::new().bold(),
strong_outer: Style::new().dimmed().hidden(),
emphasis_inner: Style::new().italic(),
emphasis_outer: Style::new().dimmed().hidden(),
strikethrough_inner: Style::new().strikethrough(),
strikethrough_outer: Style::new().dimmed().hidden(),
inline_code_inner: Style::new().bold(),
inline_code_outer: Style::new().dimmed().hidden(),
blockquote_outer: Style::new().dimmed(),
task_checked: Style::new(),
task_unchecked: Style::new().dimmed(),
list_item: Style::new().dimmed(),
rule: Style::new(),
link_outer: Style::new(),
link_text: Style::new().bold(),
link_url: Style::new().dimmed(),
link_title: Style::new(),
code_outer: Style::new().dimmed().hidden(),
code_language: Style::new().hidden(),
code_untagged: Style::new(),
code_background: Style::new(),
table_outer: Style::new().bold(),
text: Style::new(),
math: Style::new().italic(),
};
}