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