Skip to main content

conjure_cp_essence_parser/diagnostics/
source_map.rs

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 */
5use crate::diagnostics::diagnostics_api::{Position, SymbolKind};
6use rangemap::RangeMap;
7pub type SpanId = u32;
8
9#[derive(Debug, Clone)]
10pub struct HoverInfo {
11    pub description: String,       // keyword description, type info...
12    pub kind: Option<SymbolKind>,  // var, domain, function...
13    pub ty: Option<String>,        // type info like int(0..10)
14    pub decl_span: Option<SpanId>, // where declared (not sure that's doable)
15}
16// source span with start and end positions
17// in the essence source code
18#[derive(Debug, Clone)]
19pub struct SourceSpan {
20    pub start_byte: usize, // byte offset in the source code
21    pub end_byte: usize,
22    pub start_point: Position,
23    pub end_point: Position,
24    pub hover_info: Option<HoverInfo>,
25}
26
27// can add more metadata for hovering and stuff
28#[derive(Debug, Default, Clone)]
29pub struct SourceMap {
30    pub spans: Vec<SourceSpan>,
31    pub by_byte: RangeMap<usize, SpanId>,
32}
33
34// allocate a new span and return span id
35// put the position of the span in the source map
36pub fn alloc_span(
37    range: tree_sitter::Range,
38    source_map: &mut SourceMap,
39    hover_info: Option<HoverInfo>,
40) -> SpanId {
41    let span_id = source_map.spans.len() as SpanId;
42    source_map.spans.push(SourceSpan {
43        start_byte: range.start_byte,
44        end_byte: range.end_byte,
45        start_point: Position {
46            line: range.start_point.row as u32,
47            character: range.start_point.column as u32,
48        },
49        end_point: Position {
50            line: range.end_point.row as u32,
51            character: range.end_point.column as u32,
52        },
53        hover_info,
54    });
55    // map byte offsets to span id (RangeMap handles lookup)
56    // tree-sitter can generate zero-length ranges for missing tokens;
57    // avoid inserting empty ranges, which RangeMap rejects.
58    if range.start_byte < range.end_byte {
59        source_map
60            .by_byte
61            .insert(range.start_byte..range.end_byte, span_id);
62    }
63    span_id
64}
65
66impl SourceMap {
67    // helper to get hover info for a given byte offset (e.g. cursor position)
68    pub fn span_id_at_byte(&self, byte: usize) -> Option<SpanId> {
69        self.by_byte.get(&byte).copied()
70    }
71
72    // helper to get hover info for a given byte offset (e.g. cursor position)
73    pub fn hover_info_at_byte(&self, byte: usize) -> Option<&HoverInfo> {
74        self.span_id_at_byte(byte)
75            .and_then(|span_id| self.spans.get(span_id as usize))
76            .and_then(|span| span.hover_info.as_ref())
77    }
78}
79
80// helper to allocate a span with hover info directly from a tree-sitter node
81// source is not used yet but could be for more complex hover info (e.g. showing the actual code snippet)
82pub fn span_with_hover(
83    node: &tree_sitter::Node,
84    _source: &str,
85    map: &mut SourceMap,
86    info: HoverInfo,
87) -> SpanId {
88    alloc_span(node.range(), map, Some(info))
89}