use super::{convert_style, Block, Line, MetaTag, Word, SYNTHEME};
use ratatui::style::{Color, Style};
use syntect::{easy::HighlightLines, util::LinesWithEndings};
pub fn parse(fence: &mut str, code: &str) -> Block {
fence.make_ascii_lowercase();
match &*fence {
"" | "rust" | "rs" => rust(code),
_ => other(fence, code),
}
}
fn word(text: &str, style: syntect::highlighting::Style) -> Word {
Word {
word: text.into(),
style: convert_style(style),
tag: MetaTag::CodeBlock("rust".into()),
trailling_whitespace: false,
}
}
#[cold]
fn fallback(code: &str) -> Block {
code.lines()
.map(|line| Word {
word: line.into(),
style: Style {
fg: Some(Color::LightRed),
..Default::default()
},
tag: MetaTag::CodeBlock("Unknown".into()),
trailling_whitespace: false,
})
.collect()
}
pub fn rust(code: &str) -> Block {
SYNTHEME.with(|(ps, ts)| {
let Some(syntax) = ps.find_syntax_by_name("Rust") else {
return fallback(code);
};
let mut h = HighlightLines::new(syntax, &ts.themes["base16-ocean.dark"]);
let mut lines = Vec::with_capacity(8);
for line in code.lines().filter(|l| !{
let line = l.trim();
line.starts_with("# ") || line == "#"
}) {
let mut words = Vec::with_capacity(8);
for (style, text) in h.highlight_line(line, ps).unwrap() {
words.push(word(text, style));
}
lines.push(Line::from_iter(words));
}
let mut block = Block::from_iter(lines);
block.shrink_to_fit();
block
})
}
macro_rules! gen_parse_code {
($( $fname:ident ),+) => { $(
pub fn $fname(code: &str) -> Block {
SYNTHEME.with(|(ps, ts)| {
let Some(syntax) = ps.find_syntax_by_name(stringify!($fname)) else {
return rust(code);
};
gen_parse_code! { #inner code ps ts syntax }
})
}
)+ };
(#inner $code:ident $ps:ident $ts:ident $syntax:ident) => {
let mut h = HighlightLines::new($syntax, &$ts.themes["base16-ocean.dark"]);
let mut lines = Vec::with_capacity(8);
for line in LinesWithEndings::from($code) {
let mut words = Vec::with_capacity(8);
for (style, text) in h.highlight_line(line, $ps).unwrap() {
words.push(word(text, style));
}
lines.push(Line::from_iter(words));
}
let mut block = Block::from_iter(lines);
block.shrink_to_fit();
block
};
}
pub fn other(lang: &str, code: &str) -> Block {
SYNTHEME.with(|(ps, ts)| {
let Some(syntax) = ps.find_syntax_by_extension(lang) else {
return rust(code);
};
gen_parse_code! { #inner code ps ts syntax }
})
}
gen_parse_code!(markdown);
pub fn md_table(table: &str) -> Block {
markdown(table)
}