#![doc = include_str!("readme.md")]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum HighlightKind {
Heading,
Emphasis,
Strong,
Code,
Link,
ListMarker,
BlockquoteMarker,
Comment,
}
pub trait Highlighter {
fn highlight(&self, text: &str) -> Vec<(usize, usize, HighlightKind)>;
}
pub struct MarkdownHighlighter;
impl Default for MarkdownHighlighter {
fn default() -> Self {
Self
}
}
impl MarkdownHighlighter {
pub fn new() -> Self {
Self::default()
}
fn highlight_headings(&self, text: &str) -> Vec<(usize, usize, HighlightKind)> {
let mut highlights = Vec::new();
for line in text.lines() {
let trimmed = line.trim_start();
if trimmed.starts_with('#') {
let pos = text.find(line).unwrap();
highlights.push((pos, pos + line.len(), HighlightKind::Heading));
}
}
highlights
}
fn highlight_code_blocks(&self, text: &str) -> Vec<(usize, usize, HighlightKind)> {
let mut highlights = Vec::new();
let mut in_code_block = false;
let mut start_pos = 0;
for line in text.lines() {
let pos = text.find(line).unwrap();
if line.trim_start().starts_with("```") || line.trim_start().starts_with("~~~") {
if in_code_block {
highlights.push((start_pos, pos + line.len(), HighlightKind::Code));
in_code_block = false;
}
else {
start_pos = pos;
in_code_block = true;
}
}
}
highlights
}
}
impl Highlighter for MarkdownHighlighter {
fn highlight(&self, text: &str) -> Vec<(usize, usize, HighlightKind)> {
let mut highlights = Vec::new();
highlights.extend(self.highlight_headings(text));
highlights.extend(self.highlight_code_blocks(text));
highlights.sort_by_key(|&(start, _, _)| start);
highlights
}
}