Skip to main content

ochrepad_core/
editor.rs

1//! `Editor`: owns buffers and views. [`Editor::apply`] is the only path that
2//! mutates a document (PLAN 3.5).
3
4use std::collections::BTreeMap;
5use std::sync::Arc;
6use std::sync::atomic::{AtomicU64, Ordering};
7use std::time::{Duration, Instant};
8
9use crate::action::{Action, ActionError};
10use crate::buffer::{Buffer, BufferId, FileMeta, LineEdit};
11use crate::changeset::{Bias, ChangeSet};
12use crate::history::Transaction;
13use crate::position::{ByteOffset, byte_to_line_col, line_col_to_byte};
14use crate::resolve::{Resolved, resolve, resolve_search};
15use crate::selection::{Selection, SelectionSet};
16use crate::view::{View, ViewId};
17
18/// Monotonic time source for undo coalescing; replaceable in tests.
19pub trait Clock: Send {
20    fn now(&self) -> Duration;
21}
22
23pub struct SystemClock {
24    origin: Instant,
25}
26
27impl Default for SystemClock {
28    fn default() -> Self {
29        Self {
30            origin: Instant::now(),
31        }
32    }
33}
34
35impl Clock for SystemClock {
36    fn now(&self) -> Duration {
37        self.origin.elapsed()
38    }
39}
40
41/// A clock that only moves when told to. Clones share the same time.
42#[derive(Clone, Default)]
43pub struct ManualClock(Arc<AtomicU64>);
44
45impl ManualClock {
46    pub fn advance(&self, d: Duration) {
47        self.0.fetch_add(d.as_millis() as u64, Ordering::Relaxed);
48    }
49}
50
51impl Clock for ManualClock {
52    fn now(&self) -> Duration {
53        Duration::from_millis(self.0.load(Ordering::Relaxed))
54    }
55}
56
57#[derive(Clone, Debug)]
58pub struct Settings {
59    pub tab_width: usize,
60    /// The Tab key inserts spaces instead of a tab character.
61    pub use_spaces: bool,
62}
63
64impl Default for Settings {
65    fn default() -> Self {
66        Self {
67            tab_width: 4,
68            use_spaces: false,
69        }
70    }
71}
72
73pub struct Editor {
74    buffers: BTreeMap<BufferId, Buffer>,
75    views: BTreeMap<ViewId, View>,
76    next_id: u64,
77    pub settings: Settings,
78    clock: Box<dyn Clock>,
79    recording: Option<Vec<Action>>,
80}
81
82impl Default for Editor {
83    fn default() -> Self {
84        Self::with_clock(Box::new(SystemClock::default()))
85    }
86}
87
88impl Editor {
89    pub fn new() -> Self {
90        Self::default()
91    }
92
93    pub fn with_clock(clock: Box<dyn Clock>) -> Self {
94        Self {
95            buffers: BTreeMap::new(),
96            views: BTreeMap::new(),
97            next_id: 1,
98            settings: Settings::default(),
99            clock,
100            recording: None,
101        }
102    }
103
104    fn fresh_id(&mut self) -> u64 {
105        let id = self.next_id;
106        self.next_id += 1;
107        id
108    }
109
110    pub fn add_buffer(&mut self, text: &str, meta: FileMeta) -> BufferId {
111        let id = BufferId(self.fresh_id());
112        self.buffers.insert(id, Buffer::new(id, text, meta));
113        id
114    }
115
116    pub fn add_view(&mut self, buffer: BufferId) -> Result<ViewId, ActionError> {
117        if !self.buffers.contains_key(&buffer) {
118            return Err(ActionError::UnknownBuffer(buffer));
119        }
120        let id = ViewId(self.fresh_id());
121        self.views.insert(id, View::new(id, buffer));
122        Ok(id)
123    }
124
125    pub fn remove_view(&mut self, view: ViewId) -> Option<View> {
126        self.views.remove(&view)
127    }
128
129    /// Removes the buffer and every view of it.
130    pub fn remove_buffer(&mut self, buffer: BufferId) -> Option<Buffer> {
131        self.views.retain(|_, v| v.buffer() != buffer);
132        self.buffers.remove(&buffer)
133    }
134
135    pub fn buffer(&self, id: BufferId) -> Option<&Buffer> {
136        self.buffers.get(&id)
137    }
138
139    pub fn buffer_mut(&mut self, id: BufferId) -> Option<&mut Buffer> {
140        self.buffers.get_mut(&id)
141    }
142
143    pub fn view(&self, id: ViewId) -> Option<&View> {
144        self.views.get(&id)
145    }
146
147    pub fn view_mut(&mut self, id: ViewId) -> Option<&mut View> {
148        self.views.get_mut(&id)
149    }
150
151    pub fn buffers(&self) -> impl Iterator<Item = &Buffer> {
152        self.buffers.values()
153    }
154
155    pub fn buffers_mut(&mut self) -> impl Iterator<Item = &mut Buffer> {
156        self.buffers.values_mut()
157    }
158
159    pub fn views(&self) -> impl Iterator<Item = &View> {
160        self.views.values()
161    }
162
163    pub fn views_of(&self, buffer: BufferId) -> impl Iterator<Item = &View> {
164        self.views.values().filter(move |v| v.buffer() == buffer)
165    }
166
167    pub fn start_recording(&mut self) {
168        self.recording = Some(Vec::new());
169    }
170
171    pub fn is_recording(&self) -> bool {
172        self.recording.is_some()
173    }
174
175    pub fn stop_recording(&mut self) -> Vec<Action> {
176        self.recording.take().unwrap_or_default()
177    }
178
179    /// The actions recorded so far, while recording.
180    pub fn recorded(&self) -> Option<&[Action]> {
181        self.recording.as_deref()
182    }
183
184    pub(crate) fn take_recording(&mut self) -> Option<Vec<Action>> {
185        self.recording.take()
186    }
187
188    pub(crate) fn restore_recording(&mut self, recording: Option<Vec<Action>>) {
189        self.recording = recording;
190    }
191
192    /// Everything applied to `view`'s buffer until the matching
193    /// [`Self::end_undo_group`] undoes as one step.
194    pub fn begin_undo_group(&mut self, view: ViewId) -> Result<(), ActionError> {
195        let (buffer, _) = self.lookup(view)?;
196        self.buffers
197            .get_mut(&buffer)
198            .ok_or(ActionError::UnknownBuffer(buffer))?
199            .history
200            .begin_group();
201        Ok(())
202    }
203
204    pub fn end_undo_group(&mut self, view: ViewId) -> Result<(), ActionError> {
205        let (buffer, _) = self.lookup(view)?;
206        self.buffers
207            .get_mut(&buffer)
208            .ok_or(ActionError::UnknownBuffer(buffer))?
209            .history
210            .end_group();
211        Ok(())
212    }
213
214    fn lookup(&self, view: ViewId) -> Result<(BufferId, &View), ActionError> {
215        let v = self
216            .views
217            .get(&view)
218            .ok_or(ActionError::UnknownView(view))?;
219        if !self.buffers.contains_key(&v.buffer()) {
220            return Err(ActionError::UnknownBuffer(v.buffer()));
221        }
222        Ok((v.buffer(), v))
223    }
224
225    /// Resolves `action` against `view` and applies it: edits the rope, pushes
226    /// undo, maps other views' selections, marks dirty lines for the
227    /// highlighter, bumps the revision and records the action if recording.
228    pub fn apply(&mut self, view: ViewId, action: Action) -> Result<(), ActionError> {
229        let (buffer_id, _) = self.lookup(view)?;
230        match &action {
231            Action::Undo => self.undo(view, buffer_id),
232            Action::Redo => self.redo(view, buffer_id),
233            _ => {
234                let resolved = self.resolve_in(view, buffer_id, &action)??;
235                self.commit(view, buffer_id, resolved)?;
236            }
237        }
238        if let Some(rec) = &mut self.recording
239            && action.is_recordable()
240        {
241            rec.push(action);
242        }
243        Ok(())
244    }
245
246    /// Resolves `action` against the view's current state. The outer error
247    /// is a missing view/buffer; the inner one the action's own failure.
248    fn resolve_in(
249        &self,
250        view: ViewId,
251        buffer_id: BufferId,
252        action: &Action,
253    ) -> Result<Result<Resolved, ActionError>, ActionError> {
254        let buffer = self
255            .buffers
256            .get(&buffer_id)
257            .ok_or(ActionError::UnknownBuffer(buffer_id))?;
258        let sels = &self
259            .views
260            .get(&view)
261            .ok_or(ActionError::UnknownView(view))?
262            .selections;
263        Ok(match action {
264            Action::Find { .. }
265            | Action::Replace { .. }
266            | Action::ReplaceAll { .. }
267            | Action::SelectAllMatches(_) => resolve_search(action, &buffer.doc, sels),
268            _ => Ok(resolve(action, &buffer.doc, sels, &self.settings)),
269        })
270    }
271
272    /// Applies a resolved action: edits the rope, pushes undo, maps other
273    /// views' selections, bumps the revision.
274    fn commit(
275        &mut self,
276        view: ViewId,
277        buffer_id: BufferId,
278        resolved: Resolved,
279    ) -> Result<(), ActionError> {
280        match resolved {
281            Resolved::Selections(sels) => {
282                if let Some(b) = self.buffers.get_mut(&buffer_id) {
283                    b.history.break_coalescing();
284                }
285                self.set_selections(view, sels);
286            }
287            Resolved::Edit {
288                changes,
289                after,
290                kind,
291            } => {
292                let now = self.clock.now();
293                let before = self.views[&view].selections.clone();
294                let Some(buffer) = self.buffers.get_mut(&buffer_id) else {
295                    return Err(ActionError::UnknownBuffer(buffer_id));
296                };
297                let inverse = changes.invert(&buffer.doc);
298                apply_changes(buffer, &changes);
299                let rev_before = buffer.revision;
300                let rev_after = buffer.next_revision;
301                buffer.next_revision += 1;
302                buffer.revision = rev_after;
303                buffer.history.push(
304                    Transaction {
305                        changes: changes.clone(),
306                        inverse,
307                        before,
308                        after: after.clone(),
309                    },
310                    kind,
311                    rev_before,
312                    rev_after,
313                    now,
314                );
315                self.set_selections(view, after);
316                self.map_other_views(buffer_id, view, &changes);
317            }
318        }
319        Ok(())
320    }
321
322    /// Replaces a buffer's text with `text` read from disk (an external
323    /// change), as one undoable step; carets keep their line and column.
324    /// The buffer takes `meta` and is marked saved.
325    pub fn reload_buffer(
326        &mut self,
327        buffer: BufferId,
328        text: &str,
329        meta: FileMeta,
330    ) -> Result<(), ActionError> {
331        let view = self
332            .views_of(buffer)
333            .next()
334            .map(View::id)
335            .ok_or(ActionError::UnknownBuffer(buffer))?;
336        let b = self
337            .buffers
338            .get(&buffer)
339            .ok_or(ActionError::UnknownBuffer(buffer))?;
340        if b.doc.to_string() != text {
341            let new_doc = crate::text::Document::new(text);
342            let remap = |p: ByteOffset| {
343                let lc = byte_to_line_col(&b.doc, p);
344                let mut q = line_col_to_byte(&new_doc, lc);
345                while !new_doc.is_char_boundary(q) {
346                    q.0 -= 1;
347                }
348                q
349            };
350            let after = self.views[&view]
351                .selections
352                .map(|s| Selection::new(remap(s.anchor), remap(s.head)));
353            let changes = ChangeSet::new(
354                b.doc.len(),
355                vec![crate::changeset::Edit::new(
356                    ByteOffset(0),
357                    b.doc.len(),
358                    text,
359                )],
360            );
361            self.commit(
362                view,
363                buffer,
364                Resolved::Edit {
365                    changes,
366                    after,
367                    kind: crate::history::EditKind::Other,
368                },
369            )?;
370        }
371        if let Some(b) = self.buffers.get_mut(&buffer) {
372            b.meta = meta;
373            b.mark_saved();
374        }
375        Ok(())
376    }
377
378    fn undo(&mut self, view: ViewId, buffer_id: BufferId) {
379        let Some(buffer) = self.buffers.get_mut(&buffer_id) else {
380            return;
381        };
382        let Some(step) = buffer.history.pop_undo() else {
383            return;
384        };
385        let inverses: Vec<ChangeSet> = step.txns.iter().rev().map(|t| t.inverse.clone()).collect();
386        for inv in &inverses {
387            apply_changes(buffer, inv);
388        }
389        buffer.revision = step.rev_before;
390        let sels = step.txns[0].before.clone();
391        buffer.history.push_redo(step);
392        self.set_selections(view, sels);
393        for inv in &inverses {
394            self.map_other_views(buffer_id, view, inv);
395        }
396    }
397
398    fn redo(&mut self, view: ViewId, buffer_id: BufferId) {
399        let Some(buffer) = self.buffers.get_mut(&buffer_id) else {
400            return;
401        };
402        let Some(step) = buffer.history.pop_redo() else {
403            return;
404        };
405        let changes: Vec<ChangeSet> = step.txns.iter().map(|t| t.changes.clone()).collect();
406        for cs in &changes {
407            apply_changes(buffer, cs);
408        }
409        buffer.revision = step.rev_after;
410        let sels = step.txns[step.txns.len() - 1].after.clone();
411        buffer.history.push_undo(step);
412        self.set_selections(view, sels);
413        for cs in &changes {
414            self.map_other_views(buffer_id, view, cs);
415        }
416    }
417
418    fn set_selections(&mut self, view: ViewId, sels: SelectionSet) {
419        if let Some(v) = self.views.get_mut(&view) {
420            v.selections = sels;
421        }
422    }
423
424    /// Shifts the selections of every other view of `buffer` through `changes`
425    /// so their carets stay put relative to the text.
426    fn map_other_views(&mut self, buffer: BufferId, except: ViewId, changes: &ChangeSet) {
427        for v in self.views.values_mut() {
428            if v.buffer() == buffer && v.id() != except {
429                v.selections = v.selections.map(|s| Selection {
430                    anchor: changes.map_position(s.anchor, Bias::Before),
431                    head: changes.map_position(s.head, Bias::Before),
432                    goal: s.goal,
433                });
434            }
435        }
436    }
437}
438
439fn apply_changes(buffer: &mut Buffer, changes: &ChangeSet) {
440    // `ChangeSet::apply` goes back to front, so each line edit is recorded in
441    // the coordinates of the document at the moment it is applied.
442    for e in changes.edits().iter().rev() {
443        let start = buffer.doc.line_of(e.start);
444        let edit = LineEdit {
445            start,
446            old_end: buffer.doc.line_of(e.end),
447            new_end: start + e.text.bytes().filter(|&b| b == b'\n').count(),
448        };
449        buffer.record_line_edit(edit);
450    }
451    changes.apply(&mut buffer.doc);
452}