hac_core/syntax/
highlighter.rs

1use std::collections::{HashMap, VecDeque};
2use std::fmt::Debug;
3use std::sync::RwLock;
4
5use lazy_static::lazy_static;
6use ratatui::style::Style;
7use tree_sitter::{Parser, Query, QueryCursor, Tree};
8
9lazy_static! {
10    pub static ref HIGHLIGHTER: RwLock<Highlighter> = RwLock::new(Highlighter::default());
11}
12
13pub struct Highlighter {
14    parser: Parser,
15    query: Query,
16}
17
18impl Debug for Highlighter {
19    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20        f.debug_struct("Highlighter").finish()
21    }
22}
23
24#[derive(Debug, PartialEq)]
25pub struct ColorInfo {
26    pub start: usize,
27    pub end: usize,
28    pub style: Style,
29}
30
31impl Default for Highlighter {
32    fn default() -> Self {
33        let mut parser = Parser::new();
34        let json_language = include_str!("queries/json/highlights.scm");
35        let query = Query::new(&tree_sitter_json::language(), json_language)
36            .expect("failed to load json query");
37
38        parser
39            .set_language(&tree_sitter_json::language())
40            .expect("error loading json grammar");
41
42        Highlighter { parser, query }
43    }
44}
45
46impl Highlighter {
47    pub fn parse(&mut self, buffer: &str) -> Option<Tree> {
48        self.parser.parse(buffer, None)
49    }
50
51    pub fn apply(
52        &self,
53        buffer: &str,
54        tree: Option<&Tree>,
55        tokens: &HashMap<String, Style>,
56    ) -> VecDeque<ColorInfo> {
57        let mut colors = VecDeque::new();
58
59        if let Some(tree) = tree {
60            let mut cursor = QueryCursor::new();
61            let matches = cursor.matches(&self.query, tree.root_node(), buffer.as_bytes());
62
63            for m in matches {
64                for cap in m.captures {
65                    let node = cap.node;
66                    let start = node.start_byte();
67                    let end = node.end_byte();
68                    let capture_name = self.query.capture_names()[cap.index as usize];
69                    if let Some(style) = tokens.get(capture_name) {
70                        colors.push_back(ColorInfo {
71                            start,
72                            end,
73                            style: *style,
74                        });
75                    }
76                }
77            }
78        }
79
80        colors
81    }
82
83    pub fn find_indentation_level(tree: &Tree, cursor_byte_idx: usize) -> usize {
84        let root_node = tree.root_node();
85        let current_node = root_node
86            .descendant_for_byte_range(cursor_byte_idx, cursor_byte_idx)
87            .unwrap();
88        let mut indent_level: usize = 0;
89        let mut current_node = current_node;
90        while let Some(parent) = current_node.parent() {
91            if parent.kind().eq("pair") {
92                current_node = parent;
93                continue;
94            }
95            current_node = parent;
96            indent_level += 1;
97        }
98        indent_level.saturating_sub(1)
99    }
100}