Skip to main content

leekscript_analysis/
scope_extents.rs

1//! Scope extents: map from byte offset to `ScopeId` for LSP (go-to-def, references, completion).
2//!
3//! Delegates to [`sipha_analysis`] with a LeekScript-specific predicate for scope-creating nodes.
4
5use sipha::red::SyntaxNode;
6
7use leekscript_core::syntax::Kind;
8
9use super::scope::ScopeId;
10
11/// Build list of (`ScopeId`, extent) where extent is (`start_byte`, `end_byte`).
12/// Root scope (ScopeId(0)) is always first with extent (0, `source_len`).
13/// Remaining entries are in walk order, matching `scope_id_sequence`.
14#[must_use]
15pub fn build_scope_extents(
16    root: &SyntaxNode,
17    scope_id_sequence: &[ScopeId],
18    source_len: usize,
19) -> Vec<(ScopeId, (u32, u32))> {
20    sipha_analysis::build_scope_extents(
21        root,
22        ScopeId(0),
23        scope_id_sequence,
24        source_len,
25        is_scope_creating,
26    )
27}
28
29fn is_scope_creating(node: &SyntaxNode) -> bool {
30    matches!(
31        node.kind_as::<Kind>(),
32        Some(
33            Kind::NodeBlock
34                | Kind::NodeFunctionDecl
35                | Kind::NodeClassDecl
36                | Kind::NodeConstructorDecl
37                | Kind::NodeWhileStmt
38                | Kind::NodeForStmt
39                | Kind::NodeForInStmt
40                | Kind::NodeDoWhileStmt
41        )
42    )
43}
44
45/// Find the innermost scope containing the given byte offset.
46/// When extents is empty (e.g. partial state), returns root scope ID so the LSP never panics.
47#[must_use]
48pub fn scope_at_offset(extents: &[(ScopeId, (u32, u32))], offset: u32) -> ScopeId {
49    sipha_analysis::scope_at_offset(extents, offset, ScopeId(0))
50}