Skip to main content

ane/commands/syntax_engine/
merge.rs

1use crate::data::lsp::types::SemanticToken;
2
3/// LSP tokens win over tree-sitter tokens on any overlapping character range.
4/// Both inputs must be sorted by (line, start_col).
5pub fn merge(ts: &[SemanticToken], lsp: &[SemanticToken]) -> Vec<SemanticToken> {
6    let mut result = Vec::with_capacity(ts.len() + lsp.len());
7
8    for ts_tok in ts {
9        let overlaps_lsp = lsp.iter().any(|l| {
10            l.line == ts_tok.line
11                && l.start_col < ts_tok.start_col + ts_tok.length
12                && l.start_col + l.length > ts_tok.start_col
13        });
14        if !overlaps_lsp {
15            result.push(ts_tok.clone());
16        }
17    }
18
19    result.extend_from_slice(lsp);
20    result.sort_by_key(|t| (t.line, t.start_col));
21    result
22}
23
24#[cfg(test)]
25mod tests {
26    use super::*;
27
28    fn tok(line: usize, start_col: usize, length: usize, token_type: &str) -> SemanticToken {
29        SemanticToken {
30            line,
31            start_col,
32            length,
33            token_type: token_type.to_string(),
34        }
35    }
36
37    #[test]
38    fn merge_lsp_wins_on_overlap() {
39        // ts token at (line 3, col 5, len 4); lsp at (line 3, col 4, len 6)
40        // lsp covers col 4-10, ts covers col 5-9 — they overlap, lsp wins
41        let ts = vec![tok(3, 5, 4, "variable")];
42        let lsp = vec![tok(3, 4, 6, "type")];
43        let result = merge(&ts, &lsp);
44        assert_eq!(result.len(), 1, "overlapping ts token should be dropped");
45        assert_eq!(result[0].start_col, 4);
46        assert_eq!(result[0].length, 6);
47        assert_eq!(result[0].token_type, "type");
48    }
49
50    #[test]
51    fn merge_non_overlapping_tokens_preserved() {
52        // ts at col 0, lsp at col 10 — no overlap, both survive, sorted
53        let ts = vec![tok(0, 0, 3, "keyword")];
54        let lsp = vec![tok(0, 10, 5, "string")];
55        let result = merge(&ts, &lsp);
56        assert_eq!(result.len(), 2);
57        assert_eq!(result[0].start_col, 0);
58        assert_eq!(result[1].start_col, 10);
59    }
60
61    #[test]
62    fn merge_lsp_only() {
63        let lsp = vec![tok(0, 0, 4, "function"), tok(1, 2, 6, "type")];
64        let result = merge(&[], &lsp);
65        assert_eq!(result.len(), 2);
66        assert_eq!(result[0].token_type, "function");
67        assert_eq!(result[1].token_type, "type");
68    }
69
70    #[test]
71    fn merge_ts_only() {
72        let ts = vec![tok(0, 0, 2, "keyword"), tok(0, 3, 4, "variable")];
73        let result = merge(&ts, &[]);
74        assert_eq!(result.len(), 2);
75        assert_eq!(result[0].token_type, "keyword");
76        assert_eq!(result[1].token_type, "variable");
77    }
78}