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 pub fn will_reload_or_quit() -> bool {
133 WILL_RELOAD_OR_QUIT.load(Ordering::Relaxed)
134 }
135
136 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 pub fn name(&self) -> String {
182 self.0.raw_read().as_ref().unwrap().0.read().name()
183 }
184
185 pub fn path(&self) -> String {
187 self.0.raw_read().as_ref().unwrap().0.read().path()
188 }
189
190 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 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 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>>>);