logicaffeine_lsp/
selection_ranges.rs1use tower_lsp::lsp_types::{Position, Range, SelectionRange};
5
6use logicaffeine_language::token::Span;
7
8use crate::document::DocumentState;
9
10pub fn selection_ranges(doc: &DocumentState, positions: &[Position]) -> Vec<SelectionRange> {
11 positions
12 .iter()
13 .map(|position| selection_chain(doc, *position))
14 .collect()
15}
16
17fn selection_chain(doc: &DocumentState, position: Position) -> SelectionRange {
18 let offset = doc.line_index.offset(position);
19
20 let mut spans: Vec<Span> = Vec::new();
21
22 if let Some(token) = doc
24 .tokens
25 .iter()
26 .find(|t| t.span.start <= offset && offset < t.span.end)
27 {
28 spans.push(token.span);
29 }
30
31 if let Some((_, span)) = doc
33 .symbol_index
34 .statement_spans
35 .iter()
36 .filter(|(_, s)| s.start <= offset && offset < s.end)
37 .min_by_key(|(_, s)| s.end - s.start)
38 {
39 spans.push(*span);
40 }
41
42 if let Some((_, _, span)) = doc
44 .symbol_index
45 .block_spans
46 .iter()
47 .filter(|(_, _, s)| s.start <= offset && offset < s.end)
48 .min_by_key(|(_, _, s)| s.end - s.start)
49 {
50 spans.push(*span);
51 }
52
53 spans.push(Span::new(0, doc.source.len()));
55
56 let mut widening: Vec<Span> = Vec::new();
58 for span in spans {
59 match widening.last() {
60 Some(prev) if span.start <= prev.start && prev.end <= span.end && span != *prev => {
61 widening.push(span)
62 }
63 None => widening.push(span),
64 _ => {}
65 }
66 }
67
68 let mut chain: Option<SelectionRange> = None;
70 for span in widening.into_iter().rev() {
71 chain = Some(SelectionRange {
72 range: Range {
73 start: doc.line_index.position(span.start),
74 end: doc.line_index.position(span.end),
75 },
76 parent: chain.map(Box::new),
77 });
78 }
79 chain.unwrap_or(SelectionRange {
80 range: Range {
81 start: position,
82 end: position,
83 },
84 parent: None,
85 })
86}