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 fullpath_name: Option<Arc<str>>,
12    pub text: Arc<str>,
13}
14
15runmat_thread_local! {
16    static CURRENT_SOURCE: RefCell<Option<SourceInfo>> = const { RefCell::new(None) };
17    static SOURCE_CATALOG: RefCell<HashMap<SourceId, SourceInfo>> = RefCell::new(HashMap::new());
18}
19
20pub struct SourceContextGuard {
21    prev: Option<SourceInfo>,
22}
23
24impl Drop for SourceContextGuard {
25    fn drop(&mut self) {
26        let prev = self.prev.take();
27        CURRENT_SOURCE.with(|slot| {
28            *slot.borrow_mut() = prev;
29        });
30    }
31}
32
33pub struct SourceCatalogGuard {
34    prev: HashMap<SourceId, SourceInfo>,
35}
36
37impl Drop for SourceCatalogGuard {
38    fn drop(&mut self) {
39        let prev = std::mem::take(&mut self.prev);
40        SOURCE_CATALOG.with(|catalog| {
41            *catalog.borrow_mut() = prev;
42        });
43    }
44}
45
46/// Replace the current source text for this thread.
47///
48/// This is used for UX features like "show the original expression" in legends and for
49/// diagnostics that need to slice the source by byte-span.
50pub fn replace_current_source(source: Option<&str>) -> SourceContextGuard {
51    replace_current_source_context(None, source)
52}
53
54pub fn replace_current_source_context(
55    name: Option<&str>,
56    source: Option<&str>,
57) -> SourceContextGuard {
58    let next = source.map(|text| SourceInfo {
59        source_id: None,
60        name: Arc::<str>::from(name.unwrap_or_default()),
61        fullpath_name: None,
62        text: Arc::<str>::from(text),
63    });
64    let prev = CURRENT_SOURCE.with(|slot| std::mem::replace(&mut *slot.borrow_mut(), next));
65    SourceContextGuard { prev }
66}
67
68pub fn replace_current_source_id(source_id: Option<SourceId>) -> SourceContextGuard {
69    let next =
70        source_id.and_then(|id| SOURCE_CATALOG.with(|catalog| catalog.borrow().get(&id).cloned()));
71    let prev = CURRENT_SOURCE.with(|slot| std::mem::replace(&mut *slot.borrow_mut(), next));
72    SourceContextGuard { prev }
73}
74
75pub fn replace_source_catalog(entries: Vec<(SourceId, String, String)>) -> SourceCatalogGuard {
76    replace_source_catalog_with_fullpaths(
77        entries
78            .into_iter()
79            .map(|(source_id, name, text)| (source_id, name, None, text))
80            .collect(),
81    )
82}
83
84pub fn replace_source_catalog_with_fullpaths(
85    entries: Vec<(SourceId, String, Option<String>, String)>,
86) -> SourceCatalogGuard {
87    let next = entries
88        .into_iter()
89        .map(|(source_id, name, fullpath_name, text)| {
90            (
91                source_id,
92                SourceInfo {
93                    source_id: Some(source_id),
94                    name: Arc::<str>::from(name),
95                    fullpath_name: fullpath_name.map(Arc::<str>::from),
96                    text: Arc::<str>::from(text),
97                },
98            )
99        })
100        .collect::<HashMap<_, _>>();
101    let prev = SOURCE_CATALOG.with(|catalog| std::mem::replace(&mut *catalog.borrow_mut(), next));
102    SourceCatalogGuard { prev }
103}
104
105pub fn source_catalog_entries() -> Vec<(SourceId, String, String)> {
106    source_catalog_entries_with_fullpaths()
107        .into_iter()
108        .map(|(source_id, name, _fullpath_name, text)| (source_id, name, text))
109        .collect()
110}
111
112pub fn source_catalog_entries_with_fullpaths() -> Vec<(SourceId, String, Option<String>, String)> {
113    SOURCE_CATALOG.with(|catalog| {
114        catalog
115            .borrow()
116            .iter()
117            .map(|(source_id, source)| {
118                (
119                    *source_id,
120                    source.name.to_string(),
121                    source.fullpath_name.as_ref().map(ToString::to_string),
122                    source.text.to_string(),
123                )
124            })
125            .collect()
126    })
127}
128
129pub fn current_source() -> Option<Arc<str>> {
130    CURRENT_SOURCE.with(|slot| {
131        slot.borrow()
132            .as_ref()
133            .map(|source| Arc::clone(&source.text))
134    })
135}
136
137pub fn current_source_info() -> Option<SourceInfo> {
138    CURRENT_SOURCE.with(|slot| slot.borrow().clone())
139}
140
141pub fn source_info(source_id: SourceId) -> Option<SourceInfo> {
142    SOURCE_CATALOG.with(|catalog| catalog.borrow().get(&source_id).cloned())
143}