Skip to main content

oak_pascal/lsp/highlighter/
mod.rs

1#![doc = include_str!("readme.md")]
2/// Types of elements that can be highlighted.
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4pub enum HighlightKind {
5    /// A keyword.
6    Keyword,
7    /// A string literal.
8    String,
9    /// A number literal.
10    Number,
11    /// A comment.
12    Comment,
13    /// An identifier.
14    Identifier,
15}
16
17/// A trait for highlighting text.
18pub trait Highlighter {
19    /// Highlights the given text.
20    fn highlight(&self, text: &str) -> Vec<(usize, usize, HighlightKind)>;
21}
22
23/// Syntax highlighter for Pascal.
24pub struct PascalHighlighter {
25    /// Whether to use the parser for highlighting.
26    pub use_parser: bool,
27}
28
29impl Default for PascalHighlighter {
30    fn default() -> Self {
31        Self { use_parser: false }
32    }
33}
34
35impl PascalHighlighter {
36    /// Creates a new Pascal highlighter.
37    pub fn new() -> Self {
38        Self::default()
39    }
40
41    /// Creates a new Pascal highlighter that uses the parser.
42    pub fn with_parser() -> Self {
43        Self { use_parser: true }
44    }
45
46    fn highlight_keywords(&self, text: &str) -> Vec<(usize, usize, HighlightKind)> {
47        let mut highlights = Vec::new();
48        let keywords = [
49            "and",
50            "array",
51            "as",
52            "asm",
53            "begin",
54            "case",
55            "class",
56            "const",
57            "constructor",
58            "destructor",
59            "dispinterface",
60            "div",
61            "do",
62            "downto",
63            "else",
64            "end",
65            "except",
66            "exports",
67            "file",
68            "finalization",
69            "finally",
70            "for",
71            "function",
72            "goto",
73            "if",
74            "implementation",
75            "in",
76            "inherited",
77            "initialization",
78            "inline",
79            "interface",
80            "is",
81            "label",
82            "library",
83            "mod",
84            "nil",
85            "not",
86            "object",
87            "of",
88            "or",
89            "out",
90            "packed",
91            "procedure",
92            "program",
93            "property",
94            "raise",
95            "record",
96            "repeat",
97            "resourcestring",
98            "set",
99            "shl",
100            "shr",
101            "string",
102            "then",
103            "threadvar",
104            "to",
105            "try",
106            "type",
107            "unit",
108            "until",
109            "uses",
110            "var",
111            "while",
112            "with",
113            "xor",
114        ];
115
116        for keyword in &keywords {
117            let mut start = 0;
118            while let Some(pos) = text[start..].to_lowercase().find(&keyword.to_lowercase()) {
119                let absolute_pos = start + pos;
120                let end_pos = absolute_pos + keyword.len();
121
122                let is_word_boundary_before = absolute_pos == 0 || !text.chars().nth(absolute_pos - 1).unwrap_or(' ').is_alphanumeric();
123                let is_word_boundary_after = end_pos >= text.len() || !text.chars().nth(end_pos).unwrap_or(' ').is_alphanumeric();
124
125                if is_word_boundary_before && is_word_boundary_after {
126                    highlights.push((absolute_pos, end_pos, HighlightKind::Keyword))
127                }
128
129                start = absolute_pos + 1
130            }
131        }
132
133        highlights
134    }
135}
136
137impl Highlighter for PascalHighlighter {
138    fn highlight(&self, text: &str) -> Vec<(usize, usize, HighlightKind)> {
139        let mut highlights = self.highlight_keywords(text);
140        highlights.sort_by_key(|h| h.0);
141        highlights
142    }
143}