Skip to main content

bbnf_analysis/state/
mod.rs

1pub mod ast_utils;
2pub mod diagnostics;
3pub mod parsing;
4pub mod pretty;
5pub mod types;
6
7pub use ast_utils::compute_expression_end_pub;
8pub use diagnostics::analyze;
9pub use types::*;
10
11use bbnf::types::AST;
12
13use crate::analysis::LineIndex;
14
15use diagnostics::analyze_from_cache;
16use parsing::{OwnedAst, parse_once};
17
18/// Stored per-document: raw text + analyzed info + precomputed line index + cached AST.
19pub struct DocumentState {
20    pub text: String,
21    pub info: DocumentInfo,
22    /// Precomputed line-start offsets for O(log n) position conversion.
23    pub line_index: LineIndex,
24    /// Cached parsed AST (self-referential: borrows from its own String).
25    ast_cell: OwnedAst,
26}
27
28impl DocumentState {
29    pub fn new(text: String) -> Self {
30        let line_index = LineIndex::new(&text);
31        // Parse once: the OwnedAst captures the AST, and we extract diagnostics
32        // via a Cell to avoid double-parsing.
33        let parse_diag = std::cell::Cell::new(None::<ParseDiagnostics>);
34        let ast_cell = OwnedAst::new(text.clone(), |src| {
35            let (cached, diag) = parse_once(src);
36            parse_diag.set(Some(diag));
37            cached
38        });
39        let diag = parse_diag.into_inner().unwrap();
40
41        let info = analyze_from_cache(
42            &text,
43            &line_index,
44            ast_cell.borrow_dependent().as_ref(),
45            &diag,
46        );
47        Self {
48            text,
49            info,
50            line_index,
51            ast_cell,
52        }
53    }
54
55    pub fn update(&mut self, text: String) {
56        // Fast path: if text unchanged from the last parsed source, skip everything.
57        // Compare against the ast_cell's owned string (the source the AST was built from),
58        // not self.text, because the server's did_change handler may have already mutated
59        // self.text via apply_incremental_changes before calling update().
60        if *self.ast_cell.borrow_owner() == text {
61            return;
62        }
63
64        let line_index = LineIndex::new(&text);
65        let parse_diag = std::cell::Cell::new(None::<ParseDiagnostics>);
66        let ast_cell = OwnedAst::new(text.clone(), |src| {
67            let (cached, diag) = parse_once(src);
68            parse_diag.set(Some(diag));
69            cached
70        });
71        let diag = parse_diag.into_inner().unwrap();
72
73        self.info = analyze_from_cache(
74            &text,
75            &line_index,
76            ast_cell.borrow_dependent().as_ref(),
77            &diag,
78        );
79
80        self.line_index = line_index;
81        self.ast_cell = ast_cell;
82        self.text = text;
83    }
84
85    /// Access the cached AST (borrows from internally-owned text).
86    pub fn ast(&self) -> Option<&AST<'_>> {
87        self.ast_cell.borrow_dependent().as_ref().map(|c| &c.ast)
88    }
89}