1+ use std:: fmt:: Write ;
12use std:: sync:: LazyLock ;
23
34use nom:: {
@@ -9,9 +10,9 @@ use nom::{
910} ;
1011use syntect:: {
1112 easy:: HighlightLines ,
12- highlighting:: { Style , Theme , ThemeSet } ,
13+ highlighting:: { Color , FontStyle , Style , Theme , ThemeSet } ,
1314 parsing:: { SyntaxReference , SyntaxSet } ,
14- util:: { as_24_bit_terminal_escaped , LinesWithEndings } ,
15+ util:: LinesWithEndings ,
1516} ;
1617
1718static SYNTAX_SET : LazyLock < SyntaxSet > = LazyLock :: new ( SyntaxSet :: load_defaults_newlines) ;
@@ -33,6 +34,73 @@ pub enum MarkdownElement {
3334 Code { language : String , content : String } ,
3435}
3536
37+ #[ inline]
38+ fn blend_fg_color ( fg : Color , bg : Color ) -> Color {
39+ if fg. a == 0xff {
40+ return fg;
41+ }
42+ let ratio = fg. a as u32 ;
43+ let r = ( fg. r as u32 * ratio + bg. r as u32 * ( 255 - ratio) ) / 255 ;
44+ let g = ( fg. g as u32 * ratio + bg. g as u32 * ( 255 - ratio) ) / 255 ;
45+ let b = ( fg. b as u32 * ratio + bg. b as u32 * ( 255 - ratio) ) / 255 ;
46+ Color {
47+ r : r as u8 ,
48+ g : g as u8 ,
49+ b : b as u8 ,
50+ a : 255 ,
51+ }
52+ }
53+
54+ /// Formats the styled fragments using 24-bit color terminal escape codes.
55+ /// Meant for debugging and testing.
56+ ///
57+ /// This function is currently fairly inefficient in its use of escape codes.
58+ ///
59+ /// Note that this does not currently ever un-set the color so that the end of a line will also get
60+ /// highlighted with the background. This means if you might want to use `println!("\x1b[0m");`
61+ /// after to clear the coloring.
62+ ///
63+ /// If `bg` is true then the background is also set
64+ pub fn as_24_bit_terminal_escaped ( v : & [ ( Style , & str ) ] , bg : bool ) -> String {
65+ let mut s: String = String :: new ( ) ;
66+ for & ( ref style, text) in v. iter ( ) {
67+ if bg {
68+ write ! (
69+ s,
70+ "\x1b [48;2;{};{};{}m" ,
71+ style. background. r, style. background. g, style. background. b
72+ )
73+ . unwrap ( ) ;
74+ }
75+
76+ // Add font style formatting
77+ if style. font_style . contains ( FontStyle :: BOLD ) {
78+ s. push_str ( "\x1b [1m" ) ;
79+ }
80+ if style. font_style . contains ( FontStyle :: ITALIC ) {
81+ s. push_str ( "\x1b [3m" ) ;
82+ }
83+ if style. font_style . contains ( FontStyle :: UNDERLINE ) {
84+ s. push_str ( "\x1b [4m" ) ;
85+ }
86+
87+ let fg = blend_fg_color ( style. foreground , style. background ) ;
88+ write ! ( s, "\x1b [38;2;{};{};{}m{}" , fg. r, fg. g, fg. b, text) . unwrap ( ) ;
89+
90+ // Reset only the font styles to avoid bleeding, keeping colors intact
91+ if style. font_style . contains ( FontStyle :: BOLD ) {
92+ s. push_str ( "\x1b [22m" ) ; // Reset bold
93+ }
94+ if style. font_style . contains ( FontStyle :: ITALIC ) {
95+ s. push_str ( "\x1b [23m" ) ; // Reset italic
96+ }
97+ if style. font_style . contains ( FontStyle :: UNDERLINE ) {
98+ s. push_str ( "\x1b [24m" ) ; // Reset underline
99+ }
100+ }
101+ s
102+ }
103+
36104fn parse_code_fence_start ( input : & str ) -> IResult < & str , String > {
37105 let ( input, _) = tag ( "```" ) ( input) ?;
38106 let ( input, language) = opt ( not_line_ending) ( input) ?;
0 commit comments