Skip to main content

oak_powershell/lsp/highlighter/
mod.rs

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