php_lsp/analysis/
document_highlight.rs1use tower_lsp_server::ls_types::{DocumentHighlight, DocumentHighlightKind, Position, Range};
2
3use crate::document::ast::ParsedDoc;
4use crate::lang::is_unresolvable_bareword_at;
5use crate::navigation::walk::{collect_var_refs_in_scope, refs_in_stmts};
6use crate::text::word_at_position;
7
8pub fn document_highlights(
13 source: &str,
14 doc: &ParsedDoc,
15 position: Position,
16) -> Vec<DocumentHighlight> {
17 let word = match word_at_position(source, position) {
18 Some(w) => w,
19 None => return vec![],
20 };
21
22 if is_unresolvable_bareword_at(source, position, &word) {
29 return vec![];
30 }
31
32 let word_utf16_len: u32 = word.chars().map(|c| c.len_utf16() as u32).sum();
33 let sv = doc.view();
34
35 if word.starts_with('$') {
36 let bare = word.trim_start_matches('$');
37 let byte_off = sv.byte_of_position(position) as usize;
38 let mut var_spans = Vec::new();
39 collect_var_refs_in_scope(&doc.program().stmts, bare, byte_off, &mut var_spans);
40 let starts: Vec<u32> = var_spans.iter().map(|(span, _)| span.start).collect();
41 let positions = sv.positions_of_offsets(&starts);
42 var_spans
43 .into_iter()
44 .zip(positions)
45 .map(|((_, kind), start)| {
46 let end = Position {
47 line: start.line,
48 character: start.character + word_utf16_len,
49 };
50 DocumentHighlight {
51 range: Range { start, end },
52 kind: Some(kind),
53 }
54 })
55 .collect()
56 } else {
57 let mut spans = Vec::new();
65 refs_in_stmts(doc.source(), &doc.program().stmts, &word, &mut spans);
66 let starts: Vec<u32> = spans.iter().map(|span| span.start).collect();
67 let positions = sv.positions_of_offsets(&starts);
68 spans
69 .into_iter()
70 .zip(positions)
71 .map(|(_, start)| {
72 let end = Position {
73 line: start.line,
74 character: start.character + word_utf16_len,
75 };
76 DocumentHighlight {
77 range: Range { start, end },
78 kind: Some(DocumentHighlightKind::TEXT),
79 }
80 })
81 .collect()
82 }
83}