Skip to main content

oak_php/lsp/highlighter/
mod.rs

1#![doc = include_str!("readme.md")]
2/// The kind of highlighting for a range of text.
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4pub enum HighlightKind {
5    /// A keyword.
6    Keyword,
7    /// A string literal.
8    String,
9    /// A numeric literal.
10    Number,
11    /// A comment.
12    Comment,
13    /// An identifier.
14    Identifier,
15}
16
17/// Highlighter trait
18pub trait Highlighter {
19    /// Highlights the given text
20    fn highlight(&self, text: &str) -> Vec<(usize, usize, HighlightKind)>;
21}
22
23/// A highlighter for PHP code.
24pub struct PhpHighlighter {
25    /// Whether to use the parser for highlighting.
26    pub use_parser: bool,
27}
28
29impl Default for PhpHighlighter {
30    fn default() -> Self {
31        Self { use_parser: false }
32    }
33}
34
35impl PhpHighlighter {
36    /// Creates a new `PhpHighlighter`.
37    pub fn new() -> Self {
38        Self::default()
39    }
40
41    /// Creates a new `PhpHighlighter` 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            "abstract",
50            "and",
51            "array",
52            "as",
53            "break",
54            "callable",
55            "case",
56            "catch",
57            "class",
58            "clone",
59            "const",
60            "continue",
61            "declare",
62            "default",
63            "die",
64            "do",
65            "echo",
66            "else",
67            "elseif",
68            "empty",
69            "enddeclare",
70            "endfor",
71            "endforeach",
72            "endif",
73            "endswitch",
74            "endwhile",
75            "eval",
76            "exit",
77            "extends",
78            "final",
79            "finally",
80            "for",
81            "foreach",
82            "function",
83            "global",
84            "goto",
85            "if",
86            "implements",
87            "include",
88            "include_once",
89            "instanceof",
90            "insteadof",
91            "interface",
92            "isset",
93            "list",
94            "namespace",
95            "new",
96            "or",
97            "print",
98            "private",
99            "protected",
100            "public",
101            "require",
102            "require_once",
103            "return",
104            "static",
105            "switch",
106            "throw",
107            "trait",
108            "try",
109            "unset",
110            "use",
111            "var",
112            "while",
113            "xor",
114            "yield",
115        ];
116
117        for keyword in &keywords {
118            let mut start = 0;
119            while let Some(pos) = text[start..].find(keyword) {
120                let absolute_pos = start + pos;
121                let end_pos = absolute_pos + keyword.len();
122
123                let is_word_boundary_before = absolute_pos == 0 || !text.chars().nth(absolute_pos - 1).unwrap_or(' ').is_alphanumeric();
124                let is_word_boundary_after = end_pos >= text.len() || !text.chars().nth(end_pos).unwrap_or(' ').is_alphanumeric();
125
126                if is_word_boundary_before && is_word_boundary_after {
127                    highlights.push((absolute_pos, end_pos, HighlightKind::Keyword));
128                }
129
130                start = absolute_pos + 1;
131            }
132        }
133
134        highlights
135    }
136}
137
138impl Highlighter for PhpHighlighter {
139    fn highlight(&self, text: &str) -> Vec<(usize, usize, HighlightKind)> {
140        let mut highlights = self.highlight_keywords(text);
141        highlights.sort_by_key(|h| h.0);
142        highlights
143    }
144}