Skip to main content

oxidoc_highlight/
token.rs

1/// Token kind produced by scanners.
2#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3pub enum TokenKind {
4    Keyword,
5    String,
6    Comment,
7    Number,
8    Function,
9    Type,
10    Operator,
11    Punctuation,
12    Property,
13    Builtin,
14    Attr,
15    Variable,
16    Plain,
17}
18
19impl TokenKind {
20    /// CSS class name for this token kind, or `None` for `Plain`.
21    pub fn class(self) -> Option<&'static str> {
22        match self {
23            Self::Keyword => Some("tok-keyword"),
24            Self::String => Some("tok-string"),
25            Self::Comment => Some("tok-comment"),
26            Self::Number => Some("tok-number"),
27            Self::Function => Some("tok-function"),
28            Self::Type => Some("tok-type"),
29            Self::Operator => Some("tok-operator"),
30            Self::Punctuation => Some("tok-punctuation"),
31            Self::Property => Some("tok-property"),
32            Self::Builtin => Some("tok-builtin"),
33            Self::Attr => Some("tok-attr"),
34            Self::Variable => Some("tok-variable"),
35            Self::Plain => None,
36        }
37    }
38}
39
40/// A token referencing a byte range in the source string.
41#[derive(Debug, Clone, PartialEq, Eq)]
42pub struct Token {
43    pub kind: TokenKind,
44    pub start: usize,
45    pub end: usize,
46}
47
48/// Render a token list to HTML. `code` is the original source.
49pub fn render(code: &str, tokens: &[Token]) -> String {
50    use crate::escape::escape_html;
51
52    let mut out = String::with_capacity(code.len() * 2);
53    let mut pos = 0;
54
55    for tok in tokens {
56        // Emit any gap as plain text
57        if tok.start > pos {
58            escape_html(&code[pos..tok.start], &mut out);
59        }
60        let text = &code[tok.start..tok.end];
61        if let Some(cls) = tok.kind.class() {
62            out.push_str("<span class=\"");
63            out.push_str(cls);
64            out.push_str("\">");
65            escape_html(text, &mut out);
66            out.push_str("</span>");
67        } else {
68            escape_html(text, &mut out);
69        }
70        pos = tok.end;
71    }
72
73    // Trailing plain text
74    if pos < code.len() {
75        escape_html(&code[pos..], &mut out);
76    }
77
78    out
79}