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).
301 lines
9.8 KiB
Rust
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(),
|
|
};
|
|
}
|