Skip to main content

editor/
document.rs

1//! The document every editor on it shares.
2//!
3//! An [`Editor`](crate::Editor) is one view of a [`Document`]: its caret, its
4//! menus, its scroll and its layout are its own, and the text, the form it is
5//! edited in, the comment anchors and the undo stack are the document's. Two
6//! editors on one document show the same text as it is typed.
7//!
8//! Each editor holds a snapshot of the document and edits that, then hands
9//! the result back; the document tells every other editor on it, which takes
10//! the new snapshot and carries its caret through what changed.
11
12use gpui::{Context, EntityId, EventEmitter};
13use markdown::{AppExt as _, Block, BlockKind, Cursor, Doc, Part, Selection, Synced, Text};
14use std::{cell::RefCell, rc::Rc};
15
16use crate::{
17    anchor::{Anchor, Delta, SourceAnchor, SourceRanges},
18    editor::{
19        Mode,
20        mode::{source_of, switch},
21    },
22    history::History,
23};
24
25/// A document, and a count of the changes made to it.
26///
27/// Reads go through `Deref`. [`Tracked::edit`] and [`Tracked::set`] are the
28/// only ways to change it, and both move [`Tracked::revision`], so something
29/// derived from a revision is current while the revision is. Cloning is
30/// cheap: the document is shared until one of the clones is edited.
31#[derive(Clone)]
32pub(crate) struct Tracked {
33    doc: Rc<Doc>,
34    revision: u64,
35}
36
37impl Tracked {
38    fn new(doc: Rc<Doc>) -> Self {
39        Self { doc, revision: 0 }
40    }
41
42    pub(crate) fn edit(&mut self) -> &mut Doc {
43        self.revision += 1;
44        Rc::make_mut(&mut self.doc)
45    }
46
47    pub(crate) fn set(&mut self, doc: Doc) {
48        *self.edit() = doc;
49    }
50
51    /// [`Self::set`] to a document held elsewhere, shared until edited.
52    pub(crate) fn share(&mut self, doc: Rc<Doc>) {
53        self.revision += 1;
54        self.doc = doc;
55    }
56
57    pub(crate) fn shared(&self) -> Rc<Doc> {
58        self.doc.clone()
59    }
60
61    /// Give an empty document a block to hold a caret, and say whether one
62    /// was needed.
63    ///
64    /// A [`Doc`] with no blocks is a legitimate document — it is what
65    /// `parse("")` returns — but it is not something that can be edited:
66    /// nothing paints, so there is no caret and no placeholder, and neither a
67    /// click nor a hit test has a target to find.
68    pub(crate) fn ensure_block(&mut self) -> bool {
69        if !self.doc.blocks.is_empty() {
70            return false;
71        }
72        self.edit()
73            .blocks
74            .push(Block::new(BlockKind::Paragraph(Text::default())));
75        true
76    }
77
78    pub(crate) fn revision(&self) -> u64 {
79        self.revision
80    }
81}
82
83impl std::ops::Deref for Tracked {
84    type Target = Doc;
85
86    fn deref(&self) -> &Doc {
87        &self.doc
88    }
89}
90
91/// A markdown document as its editors share it.
92pub struct Document {
93    pub(crate) doc: Tracked,
94    /// Which form the document is in. In [`Mode::Source`] `doc` is one fenced
95    /// block holding the markdown.
96    pub(crate) mode: Mode,
97    /// The document as markdown and as blocks. In [`Mode::Blocks`] it is kept
98    /// up with `doc` as each change is handed back; in [`Mode::Source`] it
99    /// stands where the last switch left it, and catches up with the source at
100    /// the next. Every editor on the document holds it too.
101    pub(crate) synced: Rc<RefCell<Synced>>,
102    /// The dialect this document is read and written in — the app's own marks,
103    /// taken once at construction. One document, one spelling: a document that
104    /// changed dialect between a read and a write would rewrite itself.
105    pub(crate) marks: markdown::Marks,
106    /// Comment ranges, mapped through every edit and snapshotted with the
107    /// document. Here rather than in the app because an undo restores a whole
108    /// document and leaves no delta an app could map its own copy through.
109    pub(crate) anchors: Vec<Anchor>,
110    pub(crate) history: History,
111}
112
113/// A document changed under its editors. Every editor on it but the one that
114/// changed it takes the new snapshot.
115pub struct DocumentEvent {
116    /// The editor the change came from; `None` for a change made on the
117    /// document itself.
118    pub(crate) by: Option<EntityId>,
119    pub(crate) change: Change,
120}
121
122impl DocumentEvent {
123    /// Whether the text changed: not an IME composition still under way, and
124    /// not only the anchors.
125    pub fn edited(&self) -> bool {
126        matches!(self.change, Change::Edited(_) | Change::Replaced)
127    }
128}
129
130/// What a [`DocumentEvent`] did to positions in the document.
131pub(crate) enum Change {
132    /// An edit, through these steps — what a caret elsewhere is carried
133    /// through.
134    Edited(Rc<[Delta]>),
135    /// The document was put back whole — an undo, a switch of form — and
136    /// leaves no step to carry a caret through.
137    Replaced,
138    /// An IME composition moved the text; nothing is committed yet.
139    Composing,
140    /// [`Document::reload`] read the text again from what it was saved as.
141    Reloaded,
142    /// Only the anchors moved.
143    Anchors,
144}
145
146impl EventEmitter<DocumentEvent> for Document {}
147
148impl Document {
149    pub fn new(source: &str, cx: &mut Context<Self>) -> Self {
150        let marks = cx.marks();
151        let synced = Synced::new(source, &marks);
152        let mut doc = Tracked::new(synced.doc().clone());
153        doc.ensure_block();
154        Self {
155            doc,
156            mode: Mode::default(),
157            synced: Rc::new(RefCell::new(synced)),
158            marks,
159            anchors: Vec::new(),
160            history: History::default(),
161        }
162    }
163
164    /// How many undo steps to keep. App-wide configuration would be a gpui
165    /// global alongside [`crate::init`], not a `Theme` field — the theme is
166    /// rebuilt on every light/dark switch, which would quietly reset anything
167    /// behavioural parked in it.
168    pub fn with_undo_limit(mut self, limit: usize) -> Self {
169        self.history = History::with_limit(limit);
170        self
171    }
172
173    /// Read and write this document with marks of its own, rather than the ones
174    /// [`markdown::AppExt::set_marks`] installed. For an app whose documents do
175    /// not all speak the same dialect.
176    pub fn with_marks(mut self, marks: markdown::Marks) -> Self {
177        let source = self.source();
178        self.marks = marks;
179        let synced = Synced::new(source, &self.marks);
180        self.doc.share(synced.doc().clone());
181        self.doc.ensure_block();
182        *self.synced.borrow_mut() = synced;
183        self
184    }
185
186    /// Open in [`Mode::Source`] rather than on the document — an app whose
187    /// document is a markdown file first. Nothing is recorded: this is where
188    /// the document starts, not a switch to step back over.
189    pub fn with_mode(mut self, mode: Mode) -> Self {
190        if mode != self.mode {
191            switch(
192                &mut self.doc,
193                &mut self.synced.borrow_mut(),
194                &mut self.anchors,
195                mode,
196                Default::default(),
197            );
198            self.mode = mode;
199        }
200        self
201    }
202
203    /// The document as markdown — see [`crate::Editor::source`].
204    pub fn source(&self) -> String {
205        crate::editor::mode::source(&self.doc, self.mode, &self.synced)
206    }
207
208    /// Which form the document is being edited in.
209    pub fn mode(&self) -> Mode {
210        self.mode
211    }
212
213    pub fn doc(&self) -> &Doc {
214        &self.doc
215    }
216
217    /// Read the document again from `source`, in the form it is in. The undo
218    /// stack and the anchors go with the old text.
219    pub fn reload(&mut self, source: &str, cx: &mut Context<Self>) {
220        let synced = Synced::new(source, &self.marks);
221        self.doc.share(synced.doc().clone());
222        self.doc.ensure_block();
223        *self.synced.borrow_mut() = synced;
224        self.anchors.clear();
225        if self.mode == Mode::Source {
226            switch(
227                &mut self.doc,
228                &mut self.synced.borrow_mut(),
229                &mut self.anchors,
230                Mode::Source,
231                Cursor::default(),
232            );
233        }
234        self.history.clear();
235        cx.emit(DocumentEvent {
236            by: None,
237            change: Change::Reloaded,
238        });
239        cx.notify();
240    }
241
242    /// The comment ranges, mapped up to date with the document.
243    ///
244    /// A range that reads [`Anchor::detached`] lost the words it pointed at.
245    /// It is kept rather than dropped, because whether that means "outdated" or
246    /// "resolved" is the app's question.
247    pub fn anchors(&self) -> &[Anchor] {
248        &self.anchors
249    }
250
251    /// Hand over the whole list — the app keeps the threads, this keeps their
252    /// ranges. One entry point rather than add/remove/update, since the app is
253    /// already holding the list that decides all three.
254    ///
255    /// Ranges are positions in [`Self::doc`] as it stands: the source fence in
256    /// [`Mode::Source`], the blocks otherwise.
257    pub fn set_anchors(&mut self, anchors: Vec<Anchor>, cx: &mut Context<Self>) {
258        self.anchors = anchors;
259        cx.emit(DocumentEvent {
260            by: None,
261            change: Change::Anchors,
262        });
263        cx.notify();
264    }
265
266    /// The markdown to save, and where each anchor's ends land in it, in byte
267    /// offsets, in the order of [`Self::anchors`]. A range with an end the
268    /// markdown has no place for comes out `None`.
269    ///
270    /// The string is [`Self::source`] but for whitespace a placed end keeps
271    /// from being trailing; the ranges index this string, so it is the one to
272    /// write beside them.
273    pub fn source_with_anchors(&self) -> (String, SourceRanges) {
274        let ends: Vec<_> = self
275            .anchors
276            .iter()
277            .flat_map(|anchor| {
278                let (start, end) = anchor.range.ordered();
279                [start, end]
280            })
281            .collect();
282        let (source, offsets) = match self.mode {
283            Mode::Blocks => {
284                let mut synced = self.synced.borrow_mut();
285                synced.set_doc(self.doc.shared());
286                let offsets = synced.offsets(&ends);
287                (synced.text().to_owned(), offsets)
288            }
289            Mode::Source => {
290                let source = source_of(&self.doc).to_string();
291                let offsets = ends
292                    .iter()
293                    .map(|at| (at.offset <= source.len()).then_some(at.offset))
294                    .collect();
295                (source, offsets)
296            }
297        };
298        let ranges = self
299            .anchors
300            .iter()
301            .enumerate()
302            .map(|(ix, anchor)| {
303                let range = offsets[2 * ix].zip(offsets[2 * ix + 1]);
304                (anchor.id, range.map(|(start, end)| start..end))
305            })
306            .collect();
307        (source, ranges)
308    }
309
310    /// [`Self::set_anchors`] with ranges given in byte offsets into `source`,
311    /// the markdown the document was read from. A range with an end the
312    /// document has no place for comes in detached.
313    pub fn set_source_anchors(
314        &mut self,
315        source: &str,
316        anchors: Vec<SourceAnchor>,
317        cx: &mut Context<Self>,
318    ) {
319        let offsets: Vec<usize> = anchors
320            .iter()
321            .flat_map(|anchor| [anchor.range.start, anchor.range.end])
322            .collect();
323        let ends: Vec<Option<Cursor>> = match self.mode {
324            Mode::Blocks => markdown::parse_at_many(source, &offsets, &self.marks).1,
325            Mode::Source => offsets
326                .iter()
327                .map(|offset| {
328                    (*offset <= source.len()).then(|| Cursor::new(0, Part::Code, *offset))
329                })
330                .collect(),
331        };
332        self.anchors = anchors
333            .into_iter()
334            .enumerate()
335            .map(|(ix, anchor)| {
336                let range = match (ends[2 * ix], ends[2 * ix + 1]) {
337                    (Some(start), Some(end)) => Selection::new(start, end),
338                    (surviving, other) => Selection::at(surviving.or(other).unwrap_or_default()),
339                };
340                Anchor {
341                    id: anchor.id,
342                    range: range.clamp(&self.doc),
343                    state: anchor.state,
344                    actionable: anchor.actionable,
345                }
346            })
347            .collect();
348        cx.emit(DocumentEvent {
349            by: None,
350            change: Change::Anchors,
351        });
352        cx.notify();
353    }
354}