Skip to main content

runmat_runtime/
source_context.rs

1use runmat_hir::SourceId;
2use runmat_thread_local::runmat_thread_local;
3use std::cell::RefCell;
4use std::collections::HashMap;
5use std::sync::Arc;
6
7#[derive(Debug, Clone)]
8pub struct SourceInfo {
9    pub source_id: Option<SourceId>,
10    pub name: Arc<str>,
11    pub text: Arc<str>,
12}
13
14runmat_thread_local! {
15    static CURRENT_SOURCE: RefCell<Option<SourceInfo>> = const { RefCell::new(None) };
16    static SOURCE_CATALOG: RefCell<HashMap<SourceId, SourceInfo>> = RefCell::new(HashMap::new());
17}
18
19pub struct SourceContextGuard {
20    prev: Option<SourceInfo>,
21}
22
23impl Drop for SourceContextGuard {
24    fn drop(&mut self) {
25        let prev = self.prev.take();
26        CURRENT_SOURCE.with(|slot| {
27            *slot.borrow_mut() = prev;
28        });
29    }
30}
31
32pub struct SourceCatalogGuard {
33    prev: HashMap<SourceId, SourceInfo>,
34}
35
36impl Drop for SourceCatalogGuard {
37    fn drop(&mut self) {
38        let prev = std::mem::take(&mut self.prev);
39        SOURCE_CATALOG.with(|catalog| {
40            *catalog.borrow_mut() = prev;
41        });
42    }
43}
44
45/// Replace the current source text for this thread.
46///
47/// This is used for UX features like "show the original expression" in legends and for
48/// diagnostics that need to slice the source by byte-span.
49pub fn replace_current_source(source: Option<&str>) -> SourceContextGuard {
50    replace_current_source_context(None, source)
51}
52
53pub fn replace_current_source_context(
54    name: Option<&str>,
55    source: Option<&str>,
56) -> SourceContextGuard {
57    let next = source.map(|text| SourceInfo {
58        source_id: None,
59        name: Arc::<str>::from(name.unwrap_or_default()),
60        text: Arc::<str>::from(text),
61    });
62    let prev = CURRENT_SOURCE.with(|slot| std::mem::replace(&mut *slot.borrow_mut(), next));
63    SourceContextGuard { prev }
64}
65
66pub fn replace_current_source_id(source_id: Option<SourceId>) -> SourceContextGuard {
67    let next =
68        source_id.and_then(|id| SOURCE_CATALOG.with(|catalog| catalog.borrow().get(&id).cloned()));
69    let prev = CURRENT_SOURCE.with(|slot| std::mem::replace(&mut *slot.borrow_mut(), next));
70    SourceContextGuard { prev }
71}
72
73pub fn replace_source_catalog(entries: Vec<(SourceId, String, String)>) -> SourceCatalogGuard {
74    let next = entries
75        .into_iter()
76        .map(|(source_id, name, text)| {
77            (
78                source_id,
79                SourceInfo {
80                    source_id: Some(source_id),
81                    name: Arc::<str>::from(name),
82                    text: Arc::<str>::from(text),
83                },
84            )
85        })
86        .collect::<HashMap<_, _>>();
87    let prev = SOURCE_CATALOG.with(|catalog| std::mem::replace(&mut *catalog.borrow_mut(), next));
88    SourceCatalogGuard { prev }
89}
90
91pub fn source_catalog_entries() -> Vec<(SourceId, String, String)> {
92    SOURCE_CATALOG.with(|catalog| {
93        catalog
94            .borrow()
95            .iter()
96            .map(|(source_id, source)| {
97                (*source_id, source.name.to_string(), source.text.to_string())
98            })
99            .collect()
100    })
101}
102
103pub fn current_source() -> Option<Arc<str>> {
104    CURRENT_SOURCE.with(|slot| {
105        slot.borrow()
106            .as_ref()
107            .map(|source| Arc::clone(&source.text))
108    })
109}
110
111pub fn current_source_info() -> Option<SourceInfo> {
112    CURRENT_SOURCE.with(|slot| slot.borrow().clone())
113}
114
115pub fn source_info(source_id: SourceId) -> Option<SourceInfo> {
116    SOURCE_CATALOG.with(|catalog| catalog.borrow().get(&source_id).cloned())
117}