duat_core/buffer/mod.rs
1//! The primary [`Widget`] of Duat, used to display buffers.
2//!
3//! Most extensible features of Duat have the primary purpose of
4//! serving the [`Buffer`], such as multiple [`Cursor`]s, a
5//! `History` system, [`RawArea::PrintInfo`], etc.
6//!
7//! The [`Buffer`] also provides a list of printed lines through the
8//! [`Handle::printed_lines`] method. This method is notably used by
9//! the [`LineNumbers`] widget, that shows the numbers of the
10//! currently printed lines.
11//!
12//! [`LineNumbers`]: https://docs.rs/duat/latest/duat/widgets/struct.LineNumbers.html
13//! [`Cursor`]: crate::mode::Cursor
14//! [`RawArea::PrintInfo`]: crate::ui::traits::RawArea::PrintInfo
15use std::{
16 collections::HashMap,
17 fs,
18 ops::Range,
19 path::{Path, PathBuf},
20 sync::{LazyLock, Mutex, MutexGuard},
21};
22
23use crossterm::event::{MouseButton, MouseEventKind};
24
25pub use crate::buffer::{
26 buffer_id::BufferId,
27 history::{
28 BufferParts, BufferTracker, Change, Changes, FetchedChanges, History, Moment,
29 RangesToUpdate,
30 },
31};
32use crate::{
33 context::{self, Handle, cache},
34 data::{Pass, RwData, WriteableTuple},
35 hook::{self, BufferSaved, BufferUpdated},
36 mode::{Cursor, MouseEvent, Selections},
37 opts::PrintOpts,
38 session::TwoPointsPlace,
39 text::{Bytes, Point, Strs, Text, TextMut, TextState, txt},
40 ui::{Area, Coord, PrintInfo, PrintedLine, Widget},
41};
42
43mod history;
44
45/// The widget that is used to print and edit buffers
46pub struct Buffer {
47 id: BufferId,
48 path: PathKind,
49 text: Text,
50 pub(crate) layout_order: usize,
51 history: History,
52 cached_print_info: Mutex<Option<CachedPrintInfo>>,
53 /// The [`PrintOpts`] of this [`Buffer`]
54 ///
55 /// You can use this member to change the way this `Buffer` will
56 /// be printed specifically.
57 pub opts: PrintOpts,
58 prev_opts: Mutex<PrintOpts>,
59}
60
61impl Buffer {
62 /// Returns a new [`Buffer`], private for now
63 pub(crate) fn new(path: Option<PathBuf>, opts: PrintOpts) -> Self {
64 let (text, path) = match path {
65 Some(path) => {
66 let canon_path = path.canonicalize();
67 if let Ok(path) = &canon_path
68 && let Ok(buffer) = std::fs::read_to_string(path)
69 {
70 let selections = {
71 let selection = cache::load(path).unwrap_or_default();
72 Selections::new(selection)
73 };
74 let text = Text::from_parts(Bytes::new(&buffer), selections);
75 (text, PathKind::SetExists(path.clone()))
76 } else if canon_path.is_err()
77 && let Ok(mut canon_path) = path.with_file_name(".").canonicalize()
78 {
79 canon_path.push(path.file_name().unwrap());
80 (
81 Text::with_default_main_selection(),
82 PathKind::SetAbsent(canon_path),
83 )
84 } else {
85 (Text::with_default_main_selection(), PathKind::new_unset())
86 }
87 }
88 None => (Text::with_default_main_selection(), PathKind::new_unset()),
89 };
90
91 Self {
92 id: BufferId::new(),
93 path,
94 text,
95 layout_order: 0,
96 history: History::default(),
97 cached_print_info: Mutex::new(None),
98 opts,
99 prev_opts: Mutex::new(opts),
100 }
101 }
102
103 ////////// Saving the Buffer
104
105 ////////// Path querying functions
106
107 /// The full path of the buffer.
108 ///
109 /// If there is no set path, returns `"*scratch buffer*#{id}"`.
110 pub fn path(&self) -> String {
111 self.path.path()
112 }
113
114 /// The full path of the buffer.
115 ///
116 /// Returns [`None`] if the path has not been set yet, i.e., if
117 /// the buffer is a scratch buffer.
118 pub fn path_set(&self) -> Option<String> {
119 self.path.path_set()
120 }
121
122 /// A [`Text`] from the full path of this [`PathKind`]
123 ///
124 /// # Formatting
125 ///
126 /// If the buffer's `path` was set:
127 ///
128 /// ```text
129 /// [buffer]{path}
130 /// ```
131 ///
132 /// If the buffer's `path` was not set:
133 ///
134 /// ```text
135 /// [buffer.new.scratch]*scratch buffer #{id}*
136 /// ```
137 pub fn path_txt(&self) -> Text {
138 self.path_kind().path_txt()
139 }
140
141 /// The buffer's name.
142 ///
143 /// If there is no set path, returns `"*scratch buffer #{id}*"`.
144 pub fn name(&self) -> String {
145 self.path.name()
146 }
147
148 /// The buffer's name.
149 ///
150 /// Returns [`None`] if the path has not been set yet, i.e., if
151 /// the buffer is a scratch buffer.
152 pub fn name_set(&self) -> Option<String> {
153 self.path.name_set()
154 }
155
156 /// A [`Text`] from the name of this [`PathKind`]
157 ///
158 /// The name of a [`Buffer`] widget is the same as the path, but
159 /// it strips away the current directory. If it can't, it will
160 /// try to strip away the home directory, replacing it with
161 /// `"~"`. If that also fails, it will just show the full
162 /// path.
163 ///
164 /// # Formatting
165 ///
166 /// If the buffer's `name` was set:
167 ///
168 /// ```text
169 /// [buffer]{name}
170 /// ```
171 ///
172 /// If the buffer's `name` was not set:
173 ///
174 /// ```text
175 /// [buffer.new.scratch]*scratch buffer #{id}*
176 /// ```
177 pub fn name_txt(&self) -> Text {
178 self.path.name_txt()
179 }
180
181 /// The type of [`PathBuf`]
182 ///
183 /// This represents the three possible states for a `Buffer`'s
184 /// `PathBuf`, as it could either represent a real `Buffer`,
185 /// not exist, or not have been defined yet.
186 pub fn path_kind(&self) -> PathKind {
187 self.path.clone()
188 }
189
190 ////////// Auxiliatory methods for incremental parsing
191
192 /// Resets the print info if deemed necessary, returning the final
193 /// result, as well as `true` if things have changed
194 ///
195 /// After calling this, `self.cached_print_info` is guaranteed to
196 /// be [`Some`]
197 #[track_caller]
198 fn reset_print_info_if_needed<'b>(
199 &'b self,
200 area: &Area,
201 ) -> MutexGuard<'b, Option<CachedPrintInfo>> {
202 let opts_changed = {
203 let mut prev_opts = self.prev_opts.lock().unwrap();
204 let opts_changed = *prev_opts != self.opts;
205 *prev_opts = self.opts;
206 opts_changed
207 };
208
209 let mut cached_print_info = self.cached_print_info.lock().unwrap();
210 if opts_changed
211 || cached_print_info.as_ref().is_none_or(|cpi| {
212 self.text
213 .text_state()
214 .has_structurally_changed_since(cpi.text_state)
215 || area.get_print_info() != cpi.area_print_info
216 || area.top_left() != cpi.coords.0
217 || area.bottom_right() != cpi.coords.1
218 })
219 {
220 let start = area.start_points(&self.text, self.opts).real;
221 let end = area.end_points(&self.text, self.opts).real;
222 let printed_line_numbers = area.get_printed_lines(&self.text, self.opts).unwrap();
223
224 *cached_print_info = Some(CachedPrintInfo {
225 range: start..end,
226 printed_line_numbers,
227 printed_line_ranges: None,
228 _visible_line_ranges: None,
229 text_state: self.text.text_state(),
230 area_print_info: area.get_print_info(),
231 coords: (area.top_left(), area.bottom_right()),
232 });
233 } else {
234 cached_print_info.as_mut().unwrap().text_state = self.text.text_state();
235 };
236
237 cached_print_info
238 }
239
240 ////////// General querying functions
241
242 /// A unique identifier for this [`Buffer`]
243 ///
244 /// This is more robust than identifying it by its path or name,
245 /// or event [`PathKind`], since those could change, but this
246 /// cannot.
247 pub fn buffer_id(&self) -> BufferId {
248 self.id
249 }
250
251 /// The [`Bytes`] of the [`Buffer`]'s [`Text`]
252 pub fn bytes(&self) -> &Bytes {
253 self.text.bytes()
254 }
255
256 /// The number of bytes in the buffer.
257 pub fn len_bytes(&self) -> usize {
258 self.text.len().byte()
259 }
260
261 /// The number of [`char`]s in the buffer.
262 pub fn len_chars(&self) -> usize {
263 self.text.len().char()
264 }
265
266 /// The number of lines in the buffer.
267 pub fn len_lines(&self) -> usize {
268 self.text.len().line()
269 }
270
271 /// The [`Selections`] that are used on the [`Text`]
272 pub fn selections(&self) -> &Selections {
273 self.text.selections()
274 }
275
276 /// A mutable reference to the [`Selections`]
277 pub fn selections_mut(&mut self) -> &mut Selections {
278 self.text.selections_mut()
279 }
280
281 /// Whether o not the [`Buffer`] exists or not
282 pub fn exists(&self) -> bool {
283 self.path_set()
284 .is_some_and(|p| std::fs::exists(PathBuf::from(&p)).is_ok_and(|e| e))
285 }
286
287 /// Prepare this `Buffer` for reloading
288 ///
289 /// This works by creating a new [`Buffer`], which will take
290 /// ownership of a stripped down version of this one's [`Text`]
291 pub(crate) fn prepare_for_reloading(&mut self) -> Self {
292 self.text.prepare_for_reloading();
293 Self {
294 id: self.id,
295 path: self.path.clone(),
296 text: std::mem::take(&mut self.text),
297 layout_order: self.layout_order,
298 history: std::mem::take(&mut self.history),
299 cached_print_info: Mutex::new(self.cached_print_info.lock().unwrap().take()),
300 opts: PrintOpts::default(),
301 prev_opts: Mutex::default(),
302 }
303 }
304}
305
306impl Widget for Buffer {
307 fn update(pa: &mut Pass, handle: &Handle<Self>) {
308 // Asynchronous updating of opts
309 let (buffer, area) = handle.write_with_area(pa);
310
311 if let Some(main) = buffer.text().get_main_sel() {
312 area.scroll_around_points(
313 buffer.text(),
314 main.caret().to_two_points_after(),
315 buffer.get_print_opts(),
316 );
317 }
318
319 drop(buffer.reset_print_info_if_needed(area));
320
321 hook::trigger(pa, BufferUpdated(handle.clone()));
322
323 handle.text_mut(pa).update_bounds();
324 }
325
326 fn needs_update(&self, _: &Pass) -> bool {
327 false
328 }
329
330 fn text(&self) -> &Text {
331 &self.text
332 }
333
334 fn text_mut(&mut self) -> TextMut<'_> {
335 let mut text_mut = self.text.as_mut();
336 text_mut.attach_history(&mut self.history);
337 text_mut
338 }
339
340 fn get_print_opts(&self) -> PrintOpts {
341 self.opts
342 }
343
344 fn on_mouse_event(pa: &mut Pass, handle: &Handle<Self>, event: MouseEvent) {
345 match event.kind {
346 MouseEventKind::Down(MouseButton::Left) => {
347 let point = match event.points {
348 Some(TwoPointsPlace::Within(points) | TwoPointsPlace::AheadOf(points)) => {
349 points.real
350 }
351 _ => handle.text(pa).last_point(),
352 };
353
354 handle.selections_mut(pa).remove_extras();
355 handle.edit_main(pa, |mut c| {
356 c.unset_anchor();
357 c.move_to(point)
358 })
359 }
360 MouseEventKind::Down(_) => {}
361 MouseEventKind::Up(_) => {}
362 MouseEventKind::Drag(MouseButton::Left) => {
363 let point = match event.points {
364 Some(TwoPointsPlace::Within(points) | TwoPointsPlace::AheadOf(points)) => {
365 points.real
366 }
367 _ => handle.text(pa).last_point(),
368 };
369
370 handle.selections_mut(pa).remove_extras();
371 handle.edit_main(pa, |mut c| {
372 c.set_anchor_if_needed();
373 c.move_to(point);
374 })
375 }
376 MouseEventKind::Drag(_) => {}
377 MouseEventKind::Moved => {}
378 MouseEventKind::ScrollDown => {
379 let opts = handle.opts(pa);
380 let (widget, area) = handle.write_with_area(pa);
381 area.scroll_ver(widget.text(), 3, opts);
382 }
383 MouseEventKind::ScrollUp => {
384 let opts = handle.opts(pa);
385 let (widget, area) = handle.write_with_area(pa);
386 area.scroll_ver(widget.text(), -3, opts);
387 }
388 MouseEventKind::ScrollLeft => {}
389 MouseEventKind::ScrollRight => {}
390 }
391 }
392}
393
394impl Handle {
395 /// Writes the buffer to the current [`PathBuf`], if one was set
396 pub fn save(&self, pa: &mut Pass) -> Result<bool, Text> {
397 self.save_quit(pa, false)
398 }
399
400 /// Saves and quits, resulting in no config reload
401 ///
402 /// Returns `Ok(true)` if it saved, `Ok(false)` if that wasn't
403 /// necessary, and `Err` if there was some problem.
404 pub(crate) fn save_quit(&self, pa: &mut Pass, quit: bool) -> Result<bool, Text> {
405 let buf = self.write(pa);
406
407 if let PathKind::SetExists(path) | PathKind::SetAbsent(path) = &buf.path {
408 let path = path.clone();
409 if buf.text.has_unsaved_changes() {
410 buf.text
411 .save_on(std::io::BufWriter::new(fs::File::create(&path)?))
412 .inspect(|_| buf.path = PathKind::SetExists(path.clone()))?;
413
414 hook::trigger(pa, BufferSaved((self.clone(), quit)));
415
416 Ok(true)
417 } else {
418 Ok(false)
419 }
420 } else {
421 Err(txt!("No buffer was set"))
422 }
423 }
424
425 /// Writes the buffer to the given [`Path`]
426 ///
427 /// [`Path`]: std::path::Path
428 pub fn save_to(
429 &self,
430 pa: &mut Pass,
431 path: impl AsRef<std::path::Path>,
432 ) -> std::io::Result<bool> {
433 self.save_quit_to(pa, path, false)
434 }
435
436 /// Writes the buffer to the given [`Path`]
437 ///
438 /// [`Path`]: std::path::Path
439 pub(crate) fn save_quit_to(
440 &self,
441 pa: &mut Pass,
442 path: impl AsRef<std::path::Path>,
443 quit: bool,
444 ) -> std::io::Result<bool> {
445 let buf = self.write(pa);
446
447 if buf.text.has_unsaved_changes() {
448 let path = path.as_ref();
449 let res = buf
450 .text
451 .save_on(std::io::BufWriter::new(fs::File::create(path)?));
452 buf.history.declare_saved();
453
454 if res.as_ref().is_ok() {
455 hook::trigger(pa, BufferSaved((self.clone(), quit)));
456 }
457
458 res.and(Ok(true))
459 } else {
460 Ok(false)
461 }
462 }
463
464 /// Returns the list of printed line numbers
465 ///
466 /// These are returned as a `usize`, showing the index of the line
467 /// in the buffer, and a `bool`, which is `true` when the line is
468 /// wrapped.
469 ///
470 /// If you want the actual content of these lines (as [`Strs`]s),
471 /// check out [`Handle::printed_lines`]. If you want the content
472 /// of only the _visible_ portion of these lines, check out
473 /// [`Handle::visible_lines`].
474 #[track_caller]
475 pub fn printed_line_numbers(&self, pa: &Pass) -> Vec<PrintedLine> {
476 let buffer = self.read(pa);
477 let cpi = buffer.reset_print_info_if_needed(self.area().read(pa));
478 cpi.as_ref().unwrap().printed_line_numbers.clone()
479 }
480
481 /// The printed [`Range<Point>`], from the top of the screen to
482 /// the bottom
483 ///
484 /// Do note that this includes all concealed lines and parts that
485 /// are out of screen. If you want only to include partially
486 /// visible lines, while excluding fully hidden ones, check out
487 /// [`Handle::printed_lines`]. If you want to exclude every
488 /// concealed or out of screen section, check out
489 /// [`Handle::visible_lines`].
490 pub fn full_printed_range(&self, pa: &Pass) -> Range<Point> {
491 let buffer = self.read(pa);
492 let cpi = buffer.reset_print_info_if_needed(self.area().read(pa));
493 cpi.as_ref().unwrap().range.clone()
494 }
495
496 /// Returns the list of printed lines
497 ///
498 /// These are returned as [`Strs`], which is a known subsection of
499 /// a [`Bytes`] struct, from the [`Text`].
500 ///
501 /// Note that this function returns all portions of printed lines,
502 /// not just those that are visible. This means that it will also
503 /// include partially [concealed] lines and parts of the line that
504 /// are out of screen.
505 ///
506 /// If you want a list of _only_ the visible sections, check out
507 /// [`Handle::visible_lines`].
508 ///
509 /// If you want a [`Range<Point>`] of the printed section of the
510 /// [`Text`] (including concealed lines), check out
511 /// [`Handle::full_printed_range`].
512 ///
513 /// If you just want the line numbers of the printed lines, check
514 /// out [`Handle::printed_line_numbers`].
515 ///
516 /// [concealed]: crate::text::Conceal
517 pub fn printed_lines<'b>(&'b self, pa: &'b Pass) -> Vec<Strs<'b>> {
518 let buffer = self.read(pa);
519 let mut cpi = buffer.reset_print_info_if_needed(self.area().read(pa));
520 let cpi = cpi.as_mut().unwrap();
521 let lines = &cpi.printed_line_numbers;
522
523 let printed_lines = if let Some(printed_lines) = &cpi.printed_line_ranges {
524 printed_lines
525 } else {
526 let mut last = None;
527 cpi.printed_line_ranges.insert(
528 lines
529 .iter()
530 .filter(|line| {
531 last.as_mut()
532 .is_none_or(|num| std::mem::replace(num, line.number) < line.number)
533 })
534 .map(|line| buffer.text.line_range(line.number))
535 .collect(),
536 )
537 };
538
539 printed_lines
540 .iter()
541 .map(|range| buffer.text.strs(range.clone()).unwrap())
542 .collect()
543 }
544
545 /// A list of [`Range<usize>`]s for the byte ranges of each
546 /// printed line
547 ///
548 /// This is just a shorthand for calling [`Handle::printed_lines`]
549 /// and mapping each one via [`Strs::byte_range`].
550 pub fn printed_line_ranges(&self, pa: &Pass) -> Vec<Range<usize>> {
551 let lines = self.printed_lines(pa);
552 lines.into_iter().map(|line| line.byte_range()).collect()
553 }
554
555 /// Only the visible parts of printed lines
556 ///
557 /// This is just like [`Handle::printed_lines`], but excludes
558 /// _every_ section that was concealed or is not visible on
559 /// screen.
560 pub fn visible_lines<'b>(&'b self, _: &'b Pass) -> Vec<Strs<'b>> {
561 todo!();
562 }
563}
564
565/// Represents the presence or absence of a path
566#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
567pub enum PathKind {
568 /// A [`PathBuf`] that has been defined and points to a real
569 /// buffer
570 SetExists(PathBuf),
571 /// A [`PathBuf`] that has been defined but isn't a real buffer
572 SetAbsent(PathBuf),
573 /// A [`PathBuf`] that has not been defined
574 ///
575 /// The number within represents a specific [`Buffer`], and when
576 /// printed to, for example, the [`StatusLine`], would show up as
577 /// `txt!("[buffer]*scratch buffer*#{id}")`
578 ///
579 /// [`StatusLine`]: https://docs.rs/duat/latest/duat/widgets/struct.StatusLine.html
580 NotSet(usize),
581}
582
583impl PathKind {
584 /// Returns a new unset [`PathBuf`]
585 pub(crate) fn new_unset() -> PathKind {
586 use std::sync::atomic::{AtomicUsize, Ordering};
587 static UNSET_COUNT: AtomicUsize = AtomicUsize::new(1);
588
589 PathKind::NotSet(UNSET_COUNT.fetch_add(1, Ordering::Relaxed))
590 }
591
592 /// Returns a [`PathBuf`] if `self` is [`SetExists`] or
593 /// [`SetAbsent`]
594 ///
595 /// [`SetExists`]: PathKind::SetExists
596 /// [`SetAbsent`]: PathKind::SetAbsent
597 pub fn as_path(&self) -> Option<PathBuf> {
598 match self {
599 PathKind::SetExists(path) | PathKind::SetAbsent(path) => Some(path.clone()),
600 PathKind::NotSet(_) => None,
601 }
602 }
603
604 /// The full path of the buffer.
605 ///
606 /// If there is no set path, returns `"*scratch buffer*#{id}"`.
607 pub fn path(&self) -> String {
608 match self {
609 PathKind::SetExists(path) | PathKind::SetAbsent(path) => {
610 path.to_string_lossy().to_string()
611 }
612 PathKind::NotSet(id) => {
613 format!("*scratch buffer*#{id}")
614 }
615 }
616 }
617
618 /// The full path of the buffer.
619 ///
620 /// Returns [`None`] if the path has not been set yet.
621 pub fn path_set(&self) -> Option<String> {
622 match self {
623 PathKind::SetExists(path) | PathKind::SetAbsent(path) => {
624 Some(path.to_string_lossy().to_string())
625 }
626 PathKind::NotSet(_) => None,
627 }
628 }
629
630 /// The buffer's name.
631 ///
632 /// If there is no set path, returns `"*scratch buffer #{id}*"`.
633 pub fn name(&self) -> String {
634 match self {
635 PathKind::SetExists(path) | PathKind::SetAbsent(path) => {
636 let cur_dir = context::current_dir();
637 if let Ok(path) = path.strip_prefix(cur_dir) {
638 path.to_string_lossy().to_string()
639 } else if let Some(home_dir) = dirs_next::home_dir()
640 && let Ok(path) = path.strip_prefix(home_dir)
641 {
642 Path::new("~").join(path).to_string_lossy().to_string()
643 } else {
644 path.to_string_lossy().to_string()
645 }
646 }
647 PathKind::NotSet(id) => format!("*scratch buffer #{id}*"),
648 }
649 }
650
651 /// The buffer's name.
652 ///
653 /// Returns [`None`] if the path has not been set yet.
654 pub fn name_set(&self) -> Option<String> {
655 match self {
656 PathKind::SetExists(path) | PathKind::SetAbsent(path) => {
657 let cur_dir = context::current_dir();
658 Some(if let Ok(path) = path.strip_prefix(cur_dir) {
659 path.to_string_lossy().to_string()
660 } else if let Some(home_dir) = dirs_next::home_dir()
661 && let Ok(path) = path.strip_prefix(home_dir)
662 {
663 Path::new("~").join(path).to_string_lossy().to_string()
664 } else {
665 path.to_string_lossy().to_string()
666 })
667 }
668 PathKind::NotSet(_) => None,
669 }
670 }
671
672 /// A [`Text`] from the full path of this [`PathKind`]
673 ///
674 /// # Formatting
675 ///
676 /// If the buffer's `path` was set:
677 ///
678 /// ```text
679 /// [buffer]{path}
680 /// ```
681 ///
682 /// If the buffer's `path` was not set:
683 ///
684 /// ```text
685 /// [buffer.new.scratch]*scratch buffer #{id}*
686 /// ```
687 pub fn path_txt(&self) -> Text {
688 match self {
689 PathKind::SetExists(path) | PathKind::SetAbsent(path) => txt!("[buffer]{path}"),
690 PathKind::NotSet(id) => txt!("[buffer.new.scratch]*scratch buffer #{id}*"),
691 }
692 }
693
694 /// A [`Text`] from the name of this `PathKind`
695 ///
696 /// The name of a [`Buffer`] widget is the same as the path, but
697 /// it strips away the current directory. If it can't, it will
698 /// try to strip away the home directory, replacing it with
699 /// `"~"`. If that also fails, it will just show the full
700 /// path.
701 ///
702 /// # Formatting
703 ///
704 /// If the buffer's `name` was set:
705 ///
706 /// ```text
707 /// [buffer]{name}
708 /// ```
709 ///
710 /// If the buffer's `name` was not set:
711 ///
712 /// ```text
713 /// [buffer.new.scratch]*scratch buffer #{id}*
714 /// ```
715 pub fn name_txt(&self) -> Text {
716 match self {
717 PathKind::SetExists(path) | PathKind::SetAbsent(path) => {
718 let cur_dir = context::current_dir();
719 if let Ok(path) = path.strip_prefix(cur_dir) {
720 txt!("[buffer]{path}")
721 } else if let Some(home_dir) = dirs_next::home_dir()
722 && let Ok(path) = path.strip_prefix(home_dir)
723 {
724 txt!("[buffer]{}", Path::new("~").join(path))
725 } else {
726 txt!("[buffer]{path}")
727 }
728 }
729 PathKind::NotSet(id) => txt!("[buffer.new.scratch]*scratch buffer #{id}*"),
730 }
731 }
732}
733
734impl<P: AsRef<Path>> From<P> for PathKind {
735 fn from(value: P) -> Self {
736 let path = value.as_ref();
737 if let Ok(true) = path.try_exists() {
738 PathKind::SetExists(path.into())
739 } else {
740 PathKind::SetAbsent(path.into())
741 }
742 }
743}
744
745/// Cached information about the printing of this [`Buffer`]
746struct CachedPrintInfo {
747 range: Range<Point>,
748 printed_line_numbers: Vec<PrintedLine>,
749 printed_line_ranges: Option<Vec<Range<Point>>>,
750 _visible_line_ranges: Option<Vec<Range<Point>>>,
751 text_state: TextState,
752 area_print_info: PrintInfo,
753 coords: (Coord, Coord),
754}
755
756mod buffer_id {
757 use std::sync::atomic::{AtomicUsize, Ordering};
758
759 static COUNT: AtomicUsize = AtomicUsize::new(0);
760
761 /// A unique identifier for a [`Buffer`]
762 ///
763 /// [`Buffer`]: super::Buffer
764 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
765 pub struct BufferId(usize);
766
767 impl BufferId {
768 /// Returns a new `BufferId`, uniquely identifying a
769 /// [`Buffer`]
770 ///
771 /// [`Buffer`]: super::Buffer
772 pub(super) fn new() -> Self {
773 Self(COUNT.fetch_add(1, Ordering::Relaxed))
774 }
775
776 /// Sets the minimum `BufferId`, in order to prevent conflicts
777 pub(crate) fn set_min(buffer_ids: impl Iterator<Item = BufferId>) {
778 COUNT.store(
779 buffer_ids.map(|buf_id| buf_id.0).max().unwrap_or(0) + 1,
780 Ordering::Relaxed,
781 );
782 }
783 }
784}
785
786/// A struct to associate one `T` to each [`Buffer`]
787///
788/// This is very useful to implement the "parser pattern", where you
789/// have one parser per `Buffer`, acting on changes that take place on
790/// each `Buffer`.
791pub struct PerBuffer<T: 'static>(LazyLock<RwData<HashMap<BufferId, T>>>);
792
793impl<T: 'static> PerBuffer<T> {
794 /// Returns a new mapping of [`Buffer`]s to a type `T`
795 ///
796 /// Since this function is `const`, you can conveniently place it
797 /// in a `static` variable, in order to record one `T` for every
798 /// `Buffer` that you want to associate the struct to.
799 ///
800 /// This is very useful in order to create the "parser pattern" on
801 /// a number of different `Buffer`s, letting you store things and
802 /// retrieve them as needed.
803 ///
804 /// # Note
805 ///
806 /// This function will _not_ automatically add new [`Buffer`]s to
807 /// the list. To do that, you should add a [hook] on [`Buffer`],
808 /// that calls [`PerBuffer::register`].
809 ///
810 /// Additionally, you will probably also want to setup a hook
811 /// that calls [`PerBuffer::unregister`] on [`BufferClosed`]
812 ///
813 /// [`BufferClosed`]: crate::hook::BufferClosed
814 pub const fn new() -> Self {
815 Self(LazyLock::new(RwData::default))
816 }
817
818 /// Register a [`Buffer`] with an initial value of `T`
819 ///
820 /// If there was a previous version of `T` assoiated with the
821 /// `Buffer`, then the new `T` will replace that old version.
822 ///
823 /// You should most likely call this function on the
824 /// [`WidgetOpened<Buffer>`] hook, often aliased to just
825 /// `Buffer`.
826 ///
827 /// [`WidgetOpened<Buffer>`]: crate::hook::WidgetOpened
828 pub fn register<'p>(
829 &'p self,
830 pa: &'p mut Pass,
831 handle: &'p Handle,
832 new_value: T,
833 ) -> (&'p mut T, &'p mut Buffer) {
834 let (list, buf) = pa.write_many((&*self.0, handle));
835
836 let entry = list.entry(buf.buffer_id()).insert_entry(new_value);
837
838 (entry.into_mut(), buf)
839 }
840
841 /// Unregisters a [`Buffer`]
842 ///
843 /// This will remove the `Buffer` from the list, making future
844 /// calls to [`Self::write`] return [`None`]. You should consider
845 /// doing this on the [`BufferClosed`] hook.
846 /// Returns [`None`] if the `Buffer` wasn't already [registered].
847 ///
848 /// [`BufferClosed`]: crate::hook::BufferClosed
849 /// [registered]: Self::register
850 pub fn unregister(&self, pa: &mut Pass, handle: &Handle) -> Option<T> {
851 let buf_id = handle.read(pa).buffer_id();
852 self.0.write(pa).remove(&buf_id)
853 }
854
855 /// Gets a reference to the `T` associated with a [`Buffer`]
856 ///
857 /// This function lets you bipass the normal requirement of a
858 /// [`Pass`] in order to acquire a `T` associated with any given
859 /// `Buffer`.
860 ///
861 /// For now, the two types that can be used as [`BufferPass`]es
862 /// are the [`Buffer`] itself and a [`Cursor<Buffer>`]. These
863 /// types are allowed to do this because they are impossible
864 /// to acquire without first borrowing from an
865 /// [`RwData<Buffer>`], either directly or through a [`Handle`]
866 ///
867 /// Will return [`None`] if the `Buffer` in question wasn't
868 /// [registered] or was [unregistered].
869 ///
870 /// # Note: Why is this safe?
871 ///
872 /// From the rest of the operations on this struct, you may glean
873 /// that the [`PerBuffer`] struct is backed by a [`RwData`], and
874 /// the only way to safely access the data in those is through a
875 /// [`Pass`].
876 ///
877 /// So why can you suddenly do this without a `Pass`. Basically,
878 /// since you can't construct a `Buffer`, the only way to actually
879 /// get one is by borrowing from a [`RwData<Buffer>`] or
880 /// [`Handle`].
881 ///
882 /// Given that, the [`Pass`] will already be borrowed, and the
883 /// `Buffer` will act as an "extensionto the [`Pass`]'s borrow",
884 /// and will become invalid at the same time.
885 ///
886 /// It is important to note that this is only safe because
887 /// `Buffer`s can't be acquired without a [`Pass`].
888 ///
889 /// [registered]: Self::register
890 /// [unregistered]: Self::unregister
891 pub fn get<'b>(&'b self, buffer_pass: &'b impl BufferPass) -> Option<&'b T> {
892 static PASS: Pass = unsafe { Pass::new() };
893 let list = self.0.read(&PASS);
894 list.get(&buffer_pass.buffer_id())
895 }
896
897 /// Gets a mutable reference to the `T` associated with a
898 /// [`Buffer`]
899 ///
900 /// This function lets you bipass the normal requirement of a
901 /// [`Pass`] in order to acquire a `T` associated with any given
902 /// `Buffer`.
903 ///
904 /// For now, the two types that can be used as [`BufferPass`]es
905 /// are the [`Buffer`] itself and a [`Cursor<Buffer>`]. These
906 /// types are allowed to do this because they are impossible
907 /// to acquire without first borrowing from an [`RwData<Buffer>`],
908 /// either directly or through a [`Handle`]
909 ///
910 /// Will return [`None`] if the `Buffer` in question wasn't
911 /// [registered] or was [unregistered].
912 ///
913 /// # Note: Why is this safe?
914 ///
915 /// For the same reason that [`PerBuffer::get`] is safe. However,
916 /// in order to prevent multiple borrowings from happening at the
917 /// same time, this will take a mutable borrow of the `Buffer`,
918 /// acting much like a `&mut Pass` in that regard.
919 ///
920 /// [registered]: Self::register
921 /// [unregistered]: Self::unregister
922 pub fn get_mut<'b>(&'b self, buffer: &'b mut impl BufferPass) -> Option<&'b mut T> {
923 static PASS: Pass = unsafe { Pass::new() };
924 let list = self
925 .0
926 .write(unsafe { (&raw const PASS as *mut Pass).as_mut() }.unwrap());
927 list.get_mut(&buffer.buffer_id())
928 }
929
930 /// Writes to the [`Buffer`] and the `T` at the same time
931 ///
932 /// Will return [`None`] if the `Buffer` in question wasn't
933 /// [registered] or was [unregistered].
934 ///
935 /// [registered]: Self::register
936 /// [unregistered]: Self::unregister
937 pub fn write<'p>(
938 &'p self,
939 pa: &'p mut Pass,
940 handle: &'p Handle,
941 ) -> Option<(&'p mut T, &'p mut Buffer)> {
942 let (list, buffer) = pa.write_many((&*self.0, handle));
943 Some((list.get_mut(&buffer.buffer_id())?, buffer))
944 }
945
946 /// Writes to the [`Buffer`] and a tuple of [writeable] types
947 ///
948 /// This is an extension to the [`Pass::write_many`] method,
949 /// allowing you to write to many [`RwData`]-like structs at once.
950 ///
951 /// Returns [`None`] if any two structs, either in the [`Handle`],
952 /// [tuple], or [`PerBuffer`] point to the same thing, or if the
953 /// `Buffer` wasn't [registered] or was [unregistered].
954 ///
955 /// [writeable]: crate::data::WriteableData
956 /// [registered]: Self::register
957 /// [unregistered]: Self::unregister
958 pub fn write_with<'p, Tup: WriteableTuple<'p, impl std::any::Any>>(
959 &'p self,
960 pa: &'p mut Pass,
961 handle: &'p Handle,
962 tup: Tup,
963 ) -> Option<(&'p mut T, &'p mut Buffer, Tup::Return)> {
964 let (list, buffer, ret) = pa.try_write_many((&*self.0, handle, tup)).ok()?;
965 Some((list.get_mut(&buffer.buffer_id())?, buffer, ret))
966 }
967
968 /// Tries to write to a bunch of [`Buffer`]s and their respective
969 /// `T`s
970 ///
971 /// Returns [`None`] if any two [`Handle`]s point to the same
972 /// `Buffer`, or if any of the `Buffers` weren't [registered] or
973 /// were [unregistered].
974 ///
975 /// [registered]: Self::register
976 /// [unregistered]: Self::unregister
977 pub fn write_many<'p, const N: usize>(
978 &'p self,
979 pa: &'p mut Pass,
980 handles: [&'p Handle; N],
981 ) -> Option<[(&'p mut T, &'p mut Buffer); N]> {
982 let (list, buffers) = pa.try_write_many((&*self.0, handles)).ok()?;
983 let buf_ids = buffers.each_ref().map(|buf| buf.buffer_id());
984 let values = list.get_disjoint_mut(buf_ids.each_ref());
985
986 let list = values
987 .into_iter()
988 .zip(buffers)
989 .map(|(value, buf)| value.zip(Some(buf)))
990 .collect::<Option<Vec<_>>>()?;
991
992 list.try_into().ok()
993 }
994
995 /// Fusion of [`write_many`] and [`write_with`]
996 ///
997 /// Returns [`None`] if any two structs point to the same
998 /// [`RwData`]-like struct, or if any of the `Buffers` weren't
999 /// [registered] or were [unregistered].
1000 ///
1001 /// [`write_many`]: Self::write_many
1002 /// [`write_with`]: Self::write_with
1003 /// [registered]: Self::register
1004 /// [unregistered]: Self::unregister
1005 pub fn write_many_with<'p, const N: usize, Tup: WriteableTuple<'p, impl std::any::Any>>(
1006 &'p self,
1007 pa: &'p mut Pass,
1008 handles: [&'p Handle; N],
1009 tup: Tup,
1010 ) -> Option<([(&'p mut T, &'p mut Buffer); N], Tup::Return)> {
1011 let (list, buffers, ret) = pa.try_write_many((&*self.0, handles, tup)).ok()?;
1012 let buf_ids = buffers.each_ref().map(|buf| buf.buffer_id());
1013 let values = list.get_disjoint_mut(buf_ids.each_ref());
1014
1015 let list = values
1016 .into_iter()
1017 .zip(buffers)
1018 .map(|(value, buf)| value.zip(Some(buf)))
1019 .collect::<Option<Vec<_>>>()?;
1020
1021 Some((list.try_into().ok()?, ret))
1022 }
1023}
1024
1025impl<T: 'static> Default for PerBuffer<T> {
1026 fn default() -> Self {
1027 Self::new()
1028 }
1029}
1030
1031/// An item that identifies that you are [writing] or [reading] from
1032/// an [`RwData<Buffer>`]
1033///
1034/// This trait is used exclusively by the [`PerBuffer`] struct, which
1035/// can bipass the usual requirements that [`Pass`]es need to be used
1036/// to access the data in [`RwData`]-like structs.
1037///
1038/// [writing]: RwData::write
1039/// [reading]: RwData::read
1040#[doc(hidden)]
1041pub trait BufferPass: InnerBufferPass {
1042 #[doc(hidden)]
1043 fn buffer_id(&self) -> BufferId;
1044}
1045
1046impl BufferPass for Buffer {
1047 fn buffer_id(&self) -> BufferId {
1048 Buffer::buffer_id(self)
1049 }
1050}
1051impl<'b> BufferPass for Cursor<'b, Buffer> {
1052 fn buffer_id(&self) -> BufferId {
1053 Cursor::buffer_id(self)
1054 }
1055}
1056
1057trait InnerBufferPass {}
1058
1059impl InnerBufferPass for Buffer {}
1060impl<'b> InnerBufferPass for Cursor<'b, Buffer> {}