duat_core/data/
context.rs

1use std::{
2    any::TypeId,
3    sync::{
4        LazyLock,
5        atomic::{AtomicUsize, Ordering},
6    },
7};
8
9pub use self::global::*;
10use super::{RoData, RwData, private::InnerData};
11use crate::{
12    mode::{self, Cursors},
13    ui::{Area, Ui},
14    widgets::{File, Node, Widget},
15};
16
17mod global {
18    use std::{
19        any::Any,
20        path::PathBuf,
21        sync::{
22            LazyLock, OnceLock,
23            atomic::{AtomicBool, AtomicUsize, Ordering},
24        },
25    };
26
27    use parking_lot::Mutex;
28
29    use super::{CurFile, CurWidget, FileParts, FileReader};
30    use crate::{
31        Error, Result,
32        data::RwData,
33        duat_name,
34        mode::Regular,
35        text::Text,
36        ui::{Ui, Window},
37        widgets::{File, Node},
38    };
39
40    static MODE_NAME: LazyLock<RwData<&str>> =
41        LazyLock::new(|| RwData::new(duat_name::<Regular>()));
42    static CUR_FILE: OnceLock<&(dyn Any + Send + Sync)> = OnceLock::new();
43    static CUR_WIDGET: OnceLock<&(dyn Any + Send + Sync)> = OnceLock::new();
44    static CUR_WINDOW: AtomicUsize = AtomicUsize::new(0);
45    static WINDOWS: OnceLock<&(dyn Any + Send + Sync)> = OnceLock::new();
46    static NOTIFICATIONS: LazyLock<RwData<Text>> = LazyLock::new(RwData::default);
47    static WILL_RELOAD_OR_QUIT: AtomicBool = AtomicBool::new(false);
48    static CUR_DIR: OnceLock<Mutex<PathBuf>> = OnceLock::new();
49
50    pub fn mode_name() -> &'static RwData<&'static str> {
51        &MODE_NAME
52    }
53
54    pub fn fixed_reader<U: Ui>() -> Result<FileReader<U>, File> {
55        Ok(cur_file()?.fixed_reader())
56    }
57
58    pub fn dyn_reader<U: Ui>() -> Result<FileReader<U>, File> {
59        Ok(cur_file()?.dyn_reader())
60    }
61
62    pub fn cur_file<U: Ui>() -> Result<&'static CurFile<U>, File> {
63        let cur_file = inner_cur_file();
64        cur_file.0.read().as_ref().ok_or(Error::NoFileYet)?;
65        Ok(cur_file)
66    }
67
68    pub fn cur_widget<U: Ui>() -> Result<&'static CurWidget<U>, File> {
69        let cur_widget = inner_cur_widget();
70        cur_widget.0.read().as_ref().ok_or(Error::NoWidgetYet)?;
71        Ok(cur_widget)
72    }
73
74    pub fn cur_window() -> usize {
75        CUR_WINDOW.load(Ordering::Relaxed)
76    }
77
78    pub fn cur_dir() -> PathBuf {
79        CUR_DIR
80            .get_or_init(|| Mutex::new(std::env::current_dir().unwrap()))
81            .lock()
82            .clone()
83    }
84
85    pub fn notifications() -> &'static RwData<Text> {
86        &NOTIFICATIONS
87    }
88
89    pub fn notify(msg: Text) {
90        *NOTIFICATIONS.write() = msg
91    }
92
93    pub fn setup_non_statics<U: Ui>(
94        cur_file: &'static CurFile<U>,
95        cur_widget: &'static CurWidget<U>,
96        cur_window: usize,
97        windows: &'static RwData<Vec<Window<U>>>,
98    ) {
99        CUR_FILE.set(cur_file).expect("setup ran twice");
100        CUR_WIDGET.set(cur_widget).expect("setup ran twice");
101        CUR_WINDOW.store(cur_window, Ordering::Relaxed);
102        WINDOWS.set(windows).expect("setup ran twice");
103    }
104
105    pub(crate) fn set_cur<U: Ui>(
106        parts: Option<FileParts<U>>,
107        node: Node<U>,
108    ) -> Option<(FileParts<U>, Node<U>)> {
109        let prev = parts.and_then(|p| inner_cur_file().0.write().replace(p));
110
111        prev.zip(inner_cur_widget().0.write().replace(node))
112    }
113
114    pub(crate) fn set_windows<U: Ui>(win: Vec<Window<U>>) -> &'static AtomicUsize {
115        *windows().write() = win;
116        &CUR_WINDOW
117    }
118
119    pub(crate) fn windows<U: Ui>() -> &'static RwData<Vec<Window<U>>> {
120        WINDOWS.get().unwrap().downcast_ref().expect("1 Ui only")
121    }
122
123    pub(crate) fn inner_cur_file<U: Ui>() -> &'static CurFile<U> {
124        CUR_FILE.get().unwrap().downcast_ref().expect("1 Ui only")
125    }
126
127    pub(crate) fn inner_cur_widget<U: Ui>() -> &'static CurWidget<U> {
128        CUR_WIDGET.get().unwrap().downcast_ref().expect("1 Ui only")
129    }
130
131    /// Returns `true` if Duat is about to reload
132    pub fn will_reload_or_quit() -> bool {
133        WILL_RELOAD_OR_QUIT.load(Ordering::Relaxed)
134    }
135
136    /// Orders to quit Duat
137    pub(crate) fn order_reload_or_quit() {
138        WILL_RELOAD_OR_QUIT.store(true, Ordering::Relaxed);
139        while crate::thread::still_running() {
140            std::thread::sleep(std::time::Duration::from_micros(500));
141        }
142    }
143}
144
145pub struct CurFile<U: Ui>(LazyLock<RwData<Option<FileParts<U>>>>);
146
147impl<U: Ui> CurFile<U> {
148    pub const fn new() -> Self {
149        Self(LazyLock::new(|| RwData::new(None)))
150    }
151
152    pub fn fixed_reader(&self) -> FileReader<U> {
153        let data = self.0.raw_read();
154        let (file, area, related) = data.clone().unwrap();
155        let file_state = AtomicUsize::new(file.cur_state().load(Ordering::Relaxed));
156
157        FileReader {
158            data: RoData::new(Some((file, area, related))),
159            state: file_state,
160        }
161    }
162
163    pub fn dyn_reader(&self) -> FileReader<U> {
164        let data = self.0.raw_read();
165        let (file, ..) = data.clone().unwrap();
166
167        FileReader {
168            data: RoData::from(&*self.0),
169            state: AtomicUsize::new(file.cur_state.load(Ordering::Relaxed)),
170        }
171    }
172
173    pub fn inspect<R>(&self, f: impl FnOnce(&File, &U::Area) -> R) -> R {
174        let data = self.0.raw_read();
175        data.as_ref()
176            .map(|(file, area, _)| f(&file.read(), area))
177            .unwrap()
178    }
179
180    /// The name of the active [`File`]'s file.
181    pub fn name(&self) -> String {
182        self.0.raw_read().as_ref().unwrap().0.read().name()
183    }
184
185    /// The name of the active [`File`]'s file.
186    pub fn path(&self) -> String {
187        self.0.raw_read().as_ref().unwrap().0.read().path()
188    }
189
190    // NOTE: Doesn't return result, since it is expected that widgets can
191    // only be created after the file exists.
192    pub fn file_ptr_eq(&self, other: &Node<U>) -> bool {
193        other.ptr_eq(&self.0.read().as_ref().unwrap().0)
194    }
195
196    pub(crate) fn mutate_data<R>(&self, f: impl FnOnce(&RwData<File>, &U::Area) -> R) -> R {
197        let data = self.0.raw_read();
198        let (file, area, _) = data.as_ref().unwrap();
199
200        file.mutate(|file| {
201            let cfg = file.print_cfg();
202            file.text_mut().remove_cursors(area, cfg);
203        });
204
205        let ret = f(file, area);
206
207        let mut file = file.write();
208        let cfg = file.print_cfg();
209
210        if let Some(main) = file.cursors().and_then(Cursors::get_main) {
211            area.scroll_around_point(file.text(), main.caret(), cfg);
212        }
213        file.text_mut().add_cursors(area, cfg);
214
215        <File as Widget<U>>::update(&mut file, area);
216        if !mode::has_printing_stopped() {
217            <File as Widget<U>>::print(&mut file, area);
218        }
219
220        ret
221    }
222
223    pub(crate) fn mutate_related_widget<W: Widget<U>, R>(
224        &self,
225        f: impl FnOnce(&mut W, &U::Area) -> R,
226    ) -> Option<R> {
227        let f = move |widget: &mut W, area| {
228            let cfg = widget.print_cfg();
229            widget.text_mut().remove_cursors(area, cfg);
230
231            let ret = f(widget, area);
232
233            let cfg = widget.print_cfg();
234
235            if let Some(main) = widget.cursors().and_then(Cursors::get_main) {
236                area.scroll_around_point(widget.text(), main.caret(), widget.print_cfg());
237            }
238            widget.text_mut().add_cursors(area, cfg);
239            widget.update(area);
240            if !mode::has_printing_stopped() {
241                widget.print(area);
242            }
243
244            ret
245        };
246
247        let data = self.0.raw_read();
248        let (file, area, rel) = data.as_ref().unwrap();
249
250        let rel = rel.read();
251        if file.data_is::<W>() {
252            file.mutate_as(|w| f(w, area))
253        } else {
254            rel.iter()
255                .find(|node| node.data_is::<W>())
256                .and_then(|node| {
257                    let (widget, area) = node.parts();
258                    widget.mutate_as(|w| f(w, area))
259                })
260        }
261    }
262
263    pub(crate) fn get_related_widget<W: Widget<U>>(&self) -> Option<Node<U>> {
264        let data = self.0.write();
265        let (.., related) = data.as_ref().unwrap();
266        let related = related.read();
267
268        related.iter().find(|node| node.data_is::<W>()).cloned()
269    }
270}
271
272impl<U: Ui> Default for CurFile<U> {
273    fn default() -> Self {
274        Self::new()
275    }
276}
277
278pub struct FileReader<U: Ui> {
279    data: RoData<Option<FileParts<U>>>,
280    state: AtomicUsize,
281}
282
283impl<U: Ui> FileReader<U> {
284    pub fn inspect<R>(&self, f: impl FnOnce(&File, &U::Area) -> R) -> R {
285        let data = self.data.read();
286        let (file, area, _) = data.as_ref().unwrap();
287
288        self.state
289            .store(file.cur_state().load(Ordering::Acquire), Ordering::Release);
290
291        let file = file.read();
292        f(&file, area)
293    }
294
295    pub fn inspect_related<T: 'static, R>(&self, f: impl FnOnce(&T) -> R) -> Option<R> {
296        let data = self.data.read();
297        let (file, _, related) = data.as_ref().unwrap();
298
299        if file.data_is::<T>() {
300            file.inspect_as(f)
301        } else {
302            let related = related.read();
303            related
304                .iter()
305                .find(|node| node.data_is::<T>())
306                .and_then(|w| w.inspect_as(f))
307        }
308    }
309
310    pub fn inspect_file_and<T: 'static, R>(&self, f: impl FnOnce(&File, &T) -> R) -> Option<R> {
311        let data = self.data.read();
312        let (file, _, related) = data.as_ref().unwrap();
313
314        let related = related.read();
315        related
316            .iter()
317            .find(|node| node.data_is::<T>())
318            .and_then(|node| node.inspect_as(|widget| f(&file.read(), widget)))
319    }
320
321    /// The name of the active [`File`]'s file.
322    pub fn name(&self) -> String {
323        self.data.read().as_ref().unwrap().0.read().name()
324    }
325
326    pub fn has_swapped(&self) -> bool {
327        self.data.has_changed()
328    }
329
330    pub fn has_changed(&self) -> bool {
331        // In the case where the active file has changed, this function will
332        // return true, while also making sure that the `_state` fields point
333        // to the new file.
334        let has_changed = self.data.has_changed();
335        let data = self.data.read();
336        let (file, ..) = data.as_ref().unwrap();
337
338        has_changed || {
339            let state = file.cur_state().load(Ordering::Acquire);
340            state > self.state.swap(state, Ordering::Acquire)
341        }
342    }
343}
344
345impl<U: Ui> Clone for FileReader<U> {
346    fn clone(&self) -> Self {
347        let (file, ..) = self.data.read().clone().unwrap();
348
349        Self {
350            data: self.data.clone(),
351            state: AtomicUsize::new(file.cur_state().load(Ordering::Relaxed)),
352        }
353    }
354}
355
356pub struct CurWidget<U: Ui>(LazyLock<RwData<Option<Node<U>>>>);
357
358impl<U: Ui> CurWidget<U> {
359    pub const fn new() -> Self {
360        Self(LazyLock::new(|| RwData::new(None)))
361    }
362
363    pub fn type_id(&self) -> TypeId {
364        self.0.type_id
365    }
366
367    pub fn inspect<R>(&self, f: impl FnOnce(&dyn Widget<U>, &U::Area) -> R) -> R {
368        let data = self.0.raw_read();
369        let (widget, area) = data.as_ref().unwrap().parts();
370        let widget = widget.read();
371
372        f(&*widget, area)
373    }
374
375    pub fn inspect_widget_as<W, R>(&self, f: impl FnOnce(&W, &U::Area) -> R) -> Option<R>
376    where
377        W: Widget<U>,
378    {
379        let data = self.0.raw_read();
380        let (widget, area) = data.as_ref().unwrap().parts();
381
382        widget.inspect_as::<W, R>(|widget| f(widget, area))
383    }
384
385    pub fn inspect_as<W: Widget<U>, R>(&self, f: impl FnOnce(&W, &U::Area) -> R) -> Option<R> {
386        let data = self.0.raw_read();
387        let (widget, area) = data.as_ref().unwrap().parts();
388
389        widget.inspect_as(|widget| f(widget, area))
390    }
391
392    pub(crate) fn mutate_data<R>(
393        &self,
394        f: impl FnOnce(&RwData<dyn Widget<U>>, &U::Area) -> R,
395    ) -> R {
396        let data = self.0.read();
397        let (widget, area) = data.as_ref().unwrap().parts();
398
399        f(widget, area)
400    }
401
402    pub(crate) fn mutate_data_as<W: Widget<U>, R>(
403        &self,
404        f: impl FnOnce(&RwData<W>, &U::Area) -> R,
405    ) -> Option<R> {
406        let data = self.0.read();
407        let (widget, area) = data.as_ref().unwrap().parts();
408
409        Some(f(&widget.try_downcast::<W>()?, area))
410    }
411
412    pub(crate) fn node(&self) -> Node<U> {
413        self.0.read().as_ref().unwrap().clone()
414    }
415}
416
417impl<U: Ui> Default for CurWidget<U> {
418    fn default() -> Self {
419        Self::new()
420    }
421}
422
423pub(crate) type FileParts<U> = (RwData<File>, <U as Ui>::Area, RwData<Vec<Node<U>>>);