1use gpui::SharedString;
3
4use super::{BlockEditorModel, BlockPosition, BlockSelection, CommandError, EditOrigin};
5
6#[derive(Clone, Debug)]
9pub struct SourceSnapshot {
10 revision: u64,
11 text: SharedString,
12}
13impl SourceSnapshot {
14 pub fn revision(&self) -> u64 {
15 self.revision
16 }
17 pub fn text(&self) -> &SharedString {
18 &self.text
19 }
20}
21
22pub(super) struct PrefixSnapshot {
24 pub(super) revision: u64,
25 pub(super) selection_epoch: u64,
26 pub(super) text: Option<SharedString>,
27}
28
29#[derive(Clone, Debug)]
33pub struct CapturedRange {
34 owner: u64,
35 revision: u64,
36 selection_epoch: u64,
37 range: BlockSelection,
38 expected_selection: BlockSelection,
39}
40
41#[derive(Clone, Debug, PartialEq)]
42#[non_exhaustive]
43pub enum CapturedRangeError {
44 Stale,
45 NotText,
46 InvalidRange,
47 Edit(CommandError),
48}
49
50impl BlockEditorModel {
51 pub fn source_snapshot(&self) -> SourceSnapshot {
52 let revision = self.revision();
53 if let Some(snapshot) = &*self.source_cache.borrow()
54 && snapshot.revision == revision
55 {
56 return snapshot.clone();
57 }
58 let snapshot = SourceSnapshot {
59 revision,
60 text: super::serialize::serialize(&self.doc).to_owned().into(),
61 };
62 *self.source_cache.borrow_mut() = Some(snapshot.clone());
63 snapshot
64 }
65
66 pub(super) fn capture(&self, range: BlockSelection) -> CapturedRange {
67 CapturedRange {
68 owner: self.owner,
69 revision: self.revision(),
70 selection_epoch: self.selection_epoch,
71 range,
72 expected_selection: self.selection.clone(),
73 }
74 }
75
76 pub fn capture_caret(&self) -> Result<CapturedRange, CapturedRangeError> {
78 let caret = self.caret().ok_or(CapturedRangeError::NotText)?;
79 Ok(self.capture(BlockSelection::caret(caret)))
80 }
81
82 pub fn capture_selection(&self) -> Result<CapturedRange, CapturedRangeError> {
84 if !matches!(self.selection, BlockSelection::Text { .. }) {
85 return Err(CapturedRangeError::NotText);
86 }
87 Ok(self.capture(self.selection.clone()))
88 }
89
90 pub fn caret_prefix_snapshot(&self) -> Option<SharedString> {
95 let mut cache = self.prefix_cache.borrow_mut();
96 if cache.as_ref().is_none_or(|cached| {
97 cached.revision != self.revision() || cached.selection_epoch != self.selection_epoch
98 }) {
99 *cache = Some(PrefixSnapshot {
100 revision: self.revision(),
101 selection_epoch: self.selection_epoch,
102 text: self
103 .caret_prefix()
104 .map(|text| SharedString::from(text.to_owned())),
105 });
106 }
107 cache.as_ref().and_then(|cached| cached.text.clone())
108 }
109
110 pub fn caret_prefix(&self) -> Option<&str> {
111 let caret = self.caret()?;
112 self.doc.leaf_text(caret.block)?.get(..caret.offset)
113 }
114
115 pub fn capture_suffix(&self, len: usize) -> Result<CapturedRange, CapturedRangeError> {
117 let head = self.caret().ok_or(CapturedRangeError::NotText)?;
118 let offset = head
119 .offset
120 .checked_sub(len)
121 .ok_or(CapturedRangeError::InvalidRange)?;
122 let anchor = BlockPosition::new(head.block, offset);
123 self.doc
124 .validate(anchor)
125 .map_err(|_| CapturedRangeError::InvalidRange)?;
126 Ok(self.capture(BlockSelection::Text { anchor, head }))
127 }
128
129 pub fn capture_leaf_range(
133 &self,
134 range: std::ops::Range<usize>,
135 ) -> Result<CapturedRange, CapturedRangeError> {
136 let caret = self.caret().ok_or(CapturedRangeError::NotText)?;
137 if range.start > range.end {
138 return Err(CapturedRangeError::InvalidRange);
139 }
140 let anchor = BlockPosition::new(caret.block, range.start);
141 let head = BlockPosition::new(caret.block, range.end);
142 self.doc
143 .validate(anchor)
144 .map_err(|_| CapturedRangeError::InvalidRange)?;
145 self.doc
146 .validate(head)
147 .map_err(|_| CapturedRangeError::InvalidRange)?;
148 Ok(self.capture(BlockSelection::Text { anchor, head }))
149 }
150
151 pub fn is_capture_current(&self, capture: &CapturedRange) -> bool {
152 capture.owner == self.owner
153 && capture.revision == self.revision()
154 && capture.selection_epoch == self.selection_epoch
155 && capture.expected_selection == self.selection
156 }
157
158 pub fn replace_captured(
163 &mut self,
164 capture: &mut CapturedRange,
165 text: &str,
166 ) -> Result<(), CapturedRangeError> {
167 if !self.is_capture_current(capture) {
168 return Err(CapturedRangeError::Stale);
169 }
170 let range = self
171 .doc
172 .resolve_selection(&capture.range)
173 .map_err(|_| CapturedRangeError::InvalidRange)?;
174 let before = self.selection.clone();
175 self.selection = capture.range.clone();
176 if let Err(error) = self.insert_text(text, EditOrigin::Api) {
177 self.selection = before;
178 return Err(CapturedRangeError::Edit(error));
179 }
180 let head = self.caret().ok_or(CapturedRangeError::NotText)?;
181 *capture = self.capture(BlockSelection::Text {
182 anchor: range.start,
183 head,
184 });
185 Ok(())
186 }
187
188 pub fn invalidate_captures(&mut self) {
191 self.selection_epoch += 1;
192 }
193
194 pub fn select_end(&mut self) -> Result<(), super::SelectionError> {
195 let block = *self.doc.leaves().last().unwrap();
196 let offset = self.doc.leaf_len(block).unwrap_or(0);
197 self.set_selection(BlockSelection::caret(BlockPosition::new(block, offset)))
198 }
199}