Skip to main content

oak_powershell/lsp/highlighter/
mod.rs

1#![doc = include_str!("readme.md")]
2/// Represents the kind of highlighting for a syntax element.
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 and returns a list of ranges with their highlight kind.
20    fn highlight(&self, text: &str) -> Vec<(usize, usize, HighlightKind)>;
21}
22
23/// A highlighter for the PowerShell language.
24pub struct PowerShellHighlighter {
25    /// Whether to use the parser for highlighting.
26    pub use_parser: bool,
27}
28
29impl Default for PowerShellHighlighter {
30    fn default() -> Self {
31        Self { use_parser: false }
32    }
33}
34
35impl PowerShellHighlighter {
36    /// Creates a new `PowerShellHighlighter`.
37    pub fn new() -> Self {
38        Self::default()
39    }
40
41    /// Creates a new `PowerShellHighlighter` 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            "begin",
50            "break",
51            "catch",
52            "class",
53            "continue",
54            "data",
55            "define",
56            "do",
57            "dynamicparam",
58            "else",
59            "elseif",
60            "end",
61            "exit",
62            "filter",
63            "finally",
64            "for",
65            "foreach",
66            "from",
67            "function",
68            "if",
69            "in",
70            "inline",
71            "parallel",
72            "param",
73            "process",
74            "return",
75            "switch",
76            "throw",
77            "trap",
78            "try",
79            "until",
80            "using",
81            "var",
82            "while",
83            "workflow",
84        ];
85
86        for keyword in &keywords {
87            let mut start = 0;
88            while let Some(pos) = text[start..].to_lowercase().find(&keyword.to_lowercase()) {
89                let absolute_pos = start + pos;
90                let end_pos = absolute_pos + keyword.len();
91
92                let is_word_boundary_before = absolute_pos == 0 || !text.chars().nth(absolute_pos - 1).unwrap_or(' ').is_alphanumeric();
93                let is_word_boundary_after = end_pos >= text.len() || !text.chars().nth(end_pos).unwrap_or(' ').is_alphanumeric();
94
95                if is_word_boundary_before && is_word_boundary_after {
96                    highlights.push((absolute_pos, end_pos, HighlightKind::Keyword));
97                }
98
99                start = absolute_pos + 1;
100            }
101        }
102
103        highlights
104    }
105}
106
107impl Highlighter for PowerShellHighlighter {
108    fn highlight(&self, text: &str) -> Vec<(usize, usize, HighlightKind)> {
109        let mut highlights = self.highlight_keywords(text);
110        highlights.sort_by_key(|h| h.0);
111        highlights
112    }
113}