1use std::{
2 cell::RefCell,
3 sync::{
4 Arc,
5 atomic::{AtomicU64, Ordering},
6 },
7};
8
9static NEXT_EDITOR_OWNER: AtomicU64 = AtomicU64::new(1);
10
11use gpui::SharedString;
12
13use super::{
14 command::CommandError,
15 document::{DocumentRef, EditorDocument},
16 import::parse_document,
17 kind::BlockKind,
18 marks::{MarkKind, MarkSet, MarkState},
19 position::BlockPosition,
20 selection::{BlockSelection, SelectionError},
21 transaction::{EditOrigin, Transaction, TransactionBuilder},
22 undo::BlockUndoManager,
23};
24use gpui_kit::base::text::{MarkdownExtensions, editor_host::TextMark};
25
26pub struct BlockEditorModel {
29 pub(crate) doc: EditorDocument,
30 pub(super) owner: u64,
31 pub(super) selection_epoch: u64,
32 pub(super) source_cache: RefCell<Option<super::SourceSnapshot>>,
33 pub(super) prefix_cache: RefCell<Option<super::host::PrefixSnapshot>>,
34 pub(crate) selection: BlockSelection,
35 pub(crate) stored_marks: Option<TextMark>,
37 pub(crate) history: BlockUndoManager,
38}
39
40impl BlockEditorModel {
41 pub fn from_markdown(
43 source: &str,
44 extensions: MarkdownExtensions,
45 ) -> Result<Self, SharedString> {
46 let extensions = super::import::preserve_source_extensions(extensions);
47 let doc = parse_document(source, Arc::new(extensions))?;
48 let selection = BlockSelection::caret(doc.first_caret());
49 Ok(Self {
50 doc,
51 owner: NEXT_EDITOR_OWNER.fetch_add(1, Ordering::Relaxed),
52 selection_epoch: 0,
53 source_cache: RefCell::default(),
54 prefix_cache: RefCell::default(),
55 selection,
56 stored_marks: None,
57 history: BlockUndoManager::default(),
58 })
59 }
60
61 pub fn set_source(&mut self, source: &str) -> Result<(), SharedString> {
64 let revision = self.doc.revision;
65 self.doc = parse_document(source, self.doc.extensions.clone())?;
66 self.doc.revision = revision + 1;
67 self.selection = BlockSelection::caret(self.doc.first_caret());
68 self.stored_marks = None;
69 self.history.clear();
70 Ok(())
71 }
72
73 pub fn source(&self) -> String {
75 self.source_snapshot().text().to_string()
76 }
77
78 pub fn revision(&self) -> u64 {
80 self.doc.revision
81 }
82
83 pub fn document(&self) -> DocumentRef<'_> {
85 DocumentRef { doc: &self.doc }
86 }
87
88 pub fn selection(&self) -> &BlockSelection {
90 &self.selection
91 }
92
93 pub fn set_selection(&mut self, selection: BlockSelection) -> Result<(), SelectionError> {
95 self.doc.validate_selection(&selection)?;
96 if selection != self.selection {
97 self.selection_epoch += 1;
98 self.stored_marks = None;
99 self.history.break_coalescing();
100 }
101 self.selection = selection;
102 Ok(())
103 }
104
105 pub(crate) fn delete_span(
108 &mut self,
109 anchor: BlockPosition,
110 head: BlockPosition,
111 forward: bool,
112 ) -> Result<(), super::command::CommandError> {
113 let selection = BlockSelection::Text { anchor, head };
114 self.doc
115 .validate_selection(&selection)
116 .map_err(super::command::CommandError::Selection)?;
117 self.selection_epoch += 1;
118 self.stored_marks = None;
119 self.selection = selection;
120 self.delete_as(forward, true)
121 }
122
123 pub fn caret(&self) -> Option<BlockPosition> {
125 match &self.selection {
126 BlockSelection::Text { head, .. } => Some(*head),
127 BlockSelection::Blocks { .. } | BlockSelection::Cells { .. } => None,
128 }
129 }
130
131 pub fn active_marks(&self) -> MarkSet {
133 if let Some(stored) = &self.stored_marks {
134 return MarkSet(stored.clone());
135 }
136 let Some(caret) = self.caret() else {
137 return MarkSet::default();
138 };
139 MarkSet(self.inherited_marks(caret))
140 }
141
142 pub fn mark_state(&self, kind: MarkKind) -> MarkState {
144 let Ok(range) = self.doc.resolve_selection(&self.selection) else {
145 return MarkState::Off;
146 };
147 if range.is_collapsed() {
148 return if self.active_marks().has(kind) {
149 MarkState::On
150 } else {
151 MarkState::Off
152 };
153 }
154 let (mut on, mut off) = (false, false);
155 for (block, lo, hi) in self.text_ranges(range) {
156 if let Some(inline) = self.doc.inline(block) {
157 for (piece, marks, _) in inline.pieces() {
158 if piece.start < hi && piece.end > lo {
159 if kind.is_set(marks) {
160 on = true
161 } else {
162 off = true
163 }
164 }
165 }
166 }
167 }
168 match (on, off) {
169 (true, false) => MarkState::On,
170 (true, true) => MarkState::Mixed,
171 _ => MarkState::Off,
172 }
173 }
174
175 pub fn block_kind_at_caret(&self) -> Option<BlockKind> {
177 let caret = self.caret()?;
178 self.doc.kind(caret.block).map(|kind| kind.public())
179 }
180
181 pub fn break_coalescing(&mut self) {
183 self.history.break_coalescing();
184 }
185
186 pub fn can_undo(&self) -> bool {
188 self.history.can_undo()
189 }
190
191 pub fn can_redo(&self) -> bool {
193 self.history.can_redo()
194 }
195
196 pub fn undo(&mut self) -> bool {
198 let Some(entry) = self.history.pop_undo() else {
199 return false;
200 };
201 if Transaction::replay(&entry.inverse, &mut self.doc).is_err() {
202 return false;
203 }
204 self.selection = self.valid_or_start(entry.selection_before.clone());
205 self.stored_marks = None;
206 self.history.push_redo(entry);
207 true
208 }
209
210 pub fn redo(&mut self) -> bool {
212 let Some(entry) = self.history.pop_redo() else {
213 return false;
214 };
215 if Transaction::replay(&entry.steps, &mut self.doc).is_err() {
216 return false;
217 }
218 self.selection = self.valid_or_start(entry.selection_after.clone());
219 self.stored_marks = None;
220 self.history.push_undo(entry);
221 true
222 }
223
224 fn valid_or_start(&self, selection: BlockSelection) -> BlockSelection {
225 match self.doc.validate_selection(&selection) {
226 Ok(()) => selection,
227 Err(_) => match selection {
228 BlockSelection::Text { head, .. } => BlockSelection::caret(self.doc.clamp(head)),
229 BlockSelection::Cells { head, .. } if self.doc.get(head).is_some() => {
230 BlockSelection::caret(BlockPosition::new(head, 0))
231 }
232 BlockSelection::Blocks { .. } | BlockSelection::Cells { .. } => {
233 BlockSelection::caret(self.doc.first_caret())
234 }
235 },
236 }
237 }
238
239 pub(crate) fn transact(
242 &mut self,
243 origin: EditOrigin,
244 build: impl FnOnce(&mut TransactionBuilder<'_>) -> Result<Option<BlockSelection>, CommandError>,
245 ) -> Result<bool, CommandError> {
246 let before = self.selection.clone();
247 let mut tx = TransactionBuilder::new(&mut self.doc);
248 let after = match build(&mut tx) {
249 Ok(after) => after,
250 Err(err) => {
251 tx.rollback();
252 return Err(err);
253 }
254 };
255 let tx = tx
256 .finish(origin, before.clone(), before.clone())
257 .map_err(CommandError::from)?;
258 let changed = !tx.is_empty();
259 let after = after
261 .or_else(|| tx.map_selection(&before))
262 .unwrap_or(before);
263 let after = self.valid_or_start(after);
264 let mut tx = tx;
265 tx.selection_after = after.clone();
266 self.selection = after;
267 self.history.record(tx);
268 Ok(changed)
269 }
270
271 #[cfg(test)]
272 pub(crate) fn doc(&self) -> &EditorDocument {
273 &self.doc
274 }
275}