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