Files
inkr/src/markdown/highlighter.rs

128 lines
3.2 KiB
Rust

use egui::text::{CCursorRange, LayoutJob};
use crate::markdown::Heading;
use super::{Item, Style, parse};
/// Highlight markdown, caching previous output to save CPU.
#[derive(Default)]
pub struct MemoizedHighlighter {
style: egui::Style,
code: String,
output: LayoutJob,
}
impl MemoizedHighlighter {
pub fn highlight(
&mut self,
egui_style: &egui::Style,
code: &str,
cursor: Option<CCursorRange>,
) -> LayoutJob {
if (&self.style, self.code.as_str()) != (egui_style, code) {
self.style = egui_style.clone();
code.clone_into(&mut self.code);
self.output = highlight_markdown(egui_style, code, cursor);
}
self.output.clone()
}
}
pub fn highlight_markdown(
egui_style: &egui::Style,
text: &str,
// TODO: hide special characters where cursor isn't
_cursor: Option<CCursorRange>,
) -> LayoutJob {
let mut job = LayoutJob::default();
let code_style = Style {
code: true,
..Default::default()
};
for item in parse(text) {
match item {
Item::Text { span, style } => {
job.append(&span, 0.0, format_from_style(egui_style, &style));
}
Item::CodeBlock {
all,
language: _, // TODO
code: _, // TODO
} => {
job.append(&all, 100.0, format_from_style(egui_style, &code_style));
}
}
}
job
}
fn format_from_style(egui_style: &egui::Style, style: &Style) -> egui::text::TextFormat {
use egui::{Align, Color32, Stroke, TextStyle};
let color = if style.code {
egui_style.visuals.strong_text_color() * Color32::GREEN
} else if style.strong || style.heading.is_some() {
egui_style.visuals.strong_text_color()
} else if style.quoted {
egui_style.visuals.weak_text_color()
} else {
egui_style.visuals.text_color()
};
let text_style = if let Some(heading) = style.heading {
match heading {
Heading::H1 => TextStyle::Name("H1".into()),
Heading::H2 => TextStyle::Name("H2".into()),
Heading::H3 => TextStyle::Name("H3".into()),
Heading::H4 => TextStyle::Name("H4".into()),
Heading::H5 => TextStyle::Name("H5".into()),
Heading::H6 => TextStyle::Name("H6".into()),
}
} else if style.code {
TextStyle::Monospace
} else if style.small | style.raised {
TextStyle::Small
} else {
TextStyle::Body
};
let background = if style.code {
egui_style.visuals.code_bg_color
} else {
Color32::TRANSPARENT
};
let underline = if style.underline {
Stroke::new(1.0, color)
} else {
Stroke::NONE
};
let strikethrough = if style.strikethrough {
Stroke::new(1.0, color)
} else {
Stroke::NONE
};
let valign = if style.raised {
Align::TOP
} else {
Align::BOTTOM
};
egui::text::TextFormat {
font_id: text_style.resolve(egui_style),
color,
background,
italics: style.italics,
underline,
strikethrough,
valign,
..Default::default()
}
}