logicaffeine_lsp/
document_highlights.rs1use tower_lsp::lsp_types::{DocumentHighlight, DocumentHighlightKind, Position, Range};
6
7use crate::document::DocumentState;
8use crate::semantic_tokens::find_mutation_targets;
9
10pub fn document_highlights(
11 doc: &DocumentState,
12 position: Position,
13) -> Option<Vec<DocumentHighlight>> {
14 let offset = doc.line_index.offset(position);
15
16 let def_idx = doc
19 .symbol_index
20 .references
21 .iter()
22 .find(|r| r.span.start <= offset && offset < r.span.end)
23 .and_then(|r| r.definition_idx)
24 .or_else(|| {
25 doc.symbol_index
26 .definitions
27 .iter()
28 .position(|d| d.span.start <= offset && offset < d.span.end)
29 })?;
30 let def = &doc.symbol_index.definitions[def_idx];
31
32 let writes = find_mutation_targets(&doc.tokens);
33 let to_range = |span: logicaffeine_language::token::Span| Range {
34 start: doc.line_index.position(span.start),
35 end: doc.line_index.position(span.end),
36 };
37
38 let mut highlights = Vec::new();
39 if def.span != logicaffeine_language::token::Span::default() {
40 highlights.push(DocumentHighlight {
42 range: to_range(def.span),
43 kind: Some(DocumentHighlightKind::WRITE),
44 });
45 }
46 for reference in &doc.symbol_index.references {
47 if reference.definition_idx != Some(def_idx) || reference.span.start == def.span.start {
48 continue;
49 }
50 let kind = if writes.contains(&reference.span.start) {
51 DocumentHighlightKind::WRITE
52 } else {
53 DocumentHighlightKind::READ
54 };
55 highlights.push(DocumentHighlight {
56 range: to_range(reference.span),
57 kind: Some(kind),
58 });
59 }
60
61 if highlights.is_empty() {
62 None
63 } else {
64 Some(highlights)
65 }
66}