1
/**
2
 * Source map for mapping span IDs to source code locations and related metadata.
3
 * This is used for error reporting and diagnostics.
4
 */
5
use crate::diagnostics::diagnostics_api::{Position, SymbolKind};
6
use rangemap::RangeMap;
7
pub type SpanId = u32;
8

            
9
#[derive(Debug, Clone)]
10
pub struct HoverInfo {
11
    pub description: String,       // keyword description, type info...
12
    pub doc_key: Option<String>,   // lazy-loaded docs/bits key for documentation hovers
13
    pub kind: Option<SymbolKind>,  // var, domain, function...
14
    pub ty: Option<String>,        // type info like int(0..10)
15
    pub decl_span: Option<SpanId>, // where declared (not sure that's doable)
16
}
17
// source span with start and end positions
18
// in the essence source code
19
#[derive(Debug, Clone)]
20
pub struct SourceSpan {
21
    pub start_byte: usize, // byte offset in the source code
22
    pub end_byte: usize,
23
    pub start_point: Position,
24
    pub end_point: Position,
25
    pub hover_info: Option<HoverInfo>,
26
}
27

            
28
// can add more metadata for hovering and stuff
29
#[derive(Debug, Default, Clone)]
30
pub struct SourceMap {
31
    pub spans: Vec<SourceSpan>,
32
    pub by_byte: RangeMap<usize, SpanId>,
33
}
34

            
35
// allocate a new span and return span id
36
// put the position of the span in the source map
37
403391
pub fn alloc_span(
38
403391
    range: tree_sitter::Range,
39
403391
    source_map: &mut SourceMap,
40
403391
    hover_info: Option<HoverInfo>,
41
403391
) -> SpanId {
42
403391
    let span_id = source_map.spans.len() as SpanId;
43
403391
    source_map.spans.push(SourceSpan {
44
403391
        start_byte: range.start_byte,
45
403391
        end_byte: range.end_byte,
46
403391
        start_point: Position {
47
403391
            line: range.start_point.row as u32,
48
403391
            character: range.start_point.column as u32,
49
403391
        },
50
403391
        end_point: Position {
51
403391
            line: range.end_point.row as u32,
52
403391
            character: range.end_point.column as u32,
53
403391
        },
54
403391
        hover_info,
55
403391
    });
56
    // map byte offsets to span id (RangeMap handles lookup)
57
    // tree-sitter can generate zero-length ranges for missing tokens;
58
    // avoid inserting empty ranges, which RangeMap rejects.
59
403391
    if range.start_byte < range.end_byte {
60
403352
        source_map
61
403352
            .by_byte
62
403352
            .insert(range.start_byte..range.end_byte, span_id);
63
403352
    }
64
403391
    span_id
65
403391
}
66

            
67
impl SourceMap {
68
    // helper to get hover info for a given byte offset (e.g. cursor position)
69
    pub fn span_id_at_byte(&self, byte: usize) -> Option<SpanId> {
70
        self.by_byte.get(&byte).copied()
71
    }
72

            
73
    // helper to get hover info for a given byte offset (e.g. cursor position)
74
    pub fn hover_info_at_byte(&self, byte: usize) -> Option<&HoverInfo> {
75
        self.span_id_at_byte(byte)
76
            .and_then(|span_id| self.spans.get(span_id as usize))
77
            .and_then(|span| span.hover_info.as_ref())
78
    }
79
}
80

            
81
// helper to allocate a span with hover info directly from a tree-sitter node
82
// source is not used yet but could be for more complex hover info (e.g. showing the actual code snippet)
83
403391
pub fn span_with_hover(
84
403391
    node: &tree_sitter::Node,
85
403391
    _source: &str,
86
403391
    map: &mut SourceMap,
87
403391
    info: HoverInfo,
88
403391
) -> SpanId {
89
403391
    alloc_span(node.range(), map, Some(info))
90
403391
}