1use core::ops::Range;
2use std::borrow::Cow;
3
4use epaint::{
5 Galley,
6 text::{
7 ByteIndex, ByteRangeExt as _, CharIndex, CharRange, CharRangeExt as _, cursor::CCursor,
8 },
9};
10
11const TAB_SIZE: usize = 4;
13
14use crate::{
15 text::CCursorRange,
16 text_selection::text_cursor_state::{
17 byte_index_from_char_index, ccursor_next_word, ccursor_previous_word,
18 char_index_from_byte_index, find_line_start, slice_char_range,
19 },
20};
21
22pub trait TextBuffer {
27 fn is_mutable(&self) -> bool;
29
30 fn as_str(&self) -> &str;
32
33 fn insert_text(&mut self, text: &str, char_index: CharIndex) -> usize;
41
42 fn delete_char_range(&mut self, char_range: Range<CharIndex>);
47
48 fn char_range(&self, char_range: Range<CharIndex>) -> &str {
50 slice_char_range(self.as_str(), char_range)
51 }
52
53 fn byte_index_from_char_index(&self, char_index: CharIndex) -> ByteIndex {
54 byte_index_from_char_index(self.as_str(), char_index)
55 }
56
57 fn char_index_from_byte_index(&self, byte_index: ByteIndex) -> CharIndex {
58 char_index_from_byte_index(self.as_str(), byte_index)
59 }
60
61 fn clear(&mut self) {
63 self.delete_char_range(CharRange::full(self.as_str()));
64 }
65
66 fn replace_with(&mut self, text: &str) {
68 self.clear();
69 self.insert_text(text, CharIndex(0));
70 }
71
72 fn take(&mut self) -> String {
74 let s = self.as_str().to_owned();
75 self.clear();
76 s
77 }
78
79 fn insert_text_at(&mut self, ccursor: &mut CCursor, text_to_insert: &str, char_limit: usize) {
80 if char_limit < usize::MAX {
81 let mut new_string = text_to_insert;
82 let cutoff = char_limit.saturating_sub(self.as_str().chars().count());
84
85 new_string = match new_string.char_indices().nth(cutoff) {
86 None => new_string,
87 Some((idx, _)) => &new_string[..idx],
88 };
89
90 ccursor.index += self.insert_text(new_string, ccursor.index);
91 } else {
92 ccursor.index += self.insert_text(text_to_insert, ccursor.index);
93 }
94 }
95
96 fn decrease_indentation(&mut self, ccursor: &mut CCursor) {
97 let line_start = find_line_start(self.as_str(), *ccursor);
98
99 let remove_len = if self.as_str().chars().nth(line_start.index.0) == Some('\t') {
100 Some(1)
101 } else if self
102 .as_str()
103 .chars()
104 .skip(line_start.index.0)
105 .take(TAB_SIZE)
106 .all(|c| c == ' ')
107 {
108 Some(TAB_SIZE)
109 } else {
110 None
111 };
112
113 if let Some(len) = remove_len {
114 self.delete_char_range(line_start.index..(line_start.index + len));
115 if *ccursor != line_start {
116 *ccursor -= len;
117 }
118 }
119 }
120
121 fn delete_selected(&mut self, cursor_range: &CCursorRange) -> CCursor {
122 let [min, max] = cursor_range.sorted_cursors();
123 self.delete_selected_ccursor_range([min, max])
124 }
125
126 fn delete_selected_ccursor_range(&mut self, [min, max]: [CCursor; 2]) -> CCursor {
127 self.delete_char_range(min.index..max.index);
128 CCursor {
129 index: min.index,
130 prefer_next_row: true,
131 }
132 }
133
134 fn delete_previous_char(&mut self, ccursor: CCursor) -> CCursor {
135 if CharIndex::ZERO < ccursor.index {
136 let max_ccursor = ccursor;
137 let min_ccursor = max_ccursor - 1;
138 self.delete_selected_ccursor_range([min_ccursor, max_ccursor])
139 } else {
140 ccursor
141 }
142 }
143
144 fn delete_next_char(&mut self, ccursor: CCursor) -> CCursor {
145 self.delete_selected_ccursor_range([ccursor, ccursor + 1])
146 }
147
148 fn delete_previous_word(&mut self, max_ccursor: CCursor) -> CCursor {
149 let min_ccursor = ccursor_previous_word(self.as_str(), max_ccursor);
150 self.delete_selected_ccursor_range([min_ccursor, max_ccursor])
151 }
152
153 fn delete_next_word(&mut self, min_ccursor: CCursor) -> CCursor {
154 let max_ccursor = ccursor_next_word(self.as_str(), min_ccursor);
155 self.delete_selected_ccursor_range([min_ccursor, max_ccursor])
156 }
157
158 fn delete_surrounding_chars(
165 &mut self,
166 mut cursor_range: CCursorRange,
167 before_chars: usize,
168 after_chars: usize,
169 ) -> CCursorRange {
170 let [min, max] = cursor_range.sorted_cursors();
171 if after_chars > 0 {
172 self.delete_selected_ccursor_range([max, max + after_chars]);
173 }
174 if before_chars > 0 {
175 self.delete_selected_ccursor_range([min - before_chars, min]);
176 cursor_range.primary -= before_chars;
177 cursor_range.secondary -= before_chars;
178 }
179 cursor_range
180 }
181
182 fn delete_paragraph_before_cursor(
183 &mut self,
184 galley: &Galley,
185 cursor_range: &CCursorRange,
186 ) -> CCursor {
187 let [min, max] = cursor_range.sorted_cursors();
188 let min = galley.cursor_begin_of_paragraph(&min);
189 if min == max {
190 self.delete_previous_char(min)
191 } else {
192 self.delete_selected(&CCursorRange::two(min, max))
193 }
194 }
195
196 fn delete_paragraph_after_cursor(
197 &mut self,
198 galley: &Galley,
199 cursor_range: &CCursorRange,
200 ) -> CCursor {
201 let [min, max] = cursor_range.sorted_cursors();
202 let max = galley.cursor_end_of_paragraph(&max);
203 if min == max {
204 self.delete_next_char(min)
205 } else {
206 self.delete_selected(&CCursorRange::two(min, max))
207 }
208 }
209
210 fn type_id(&self) -> core::any::TypeId;
242}
243
244impl TextBuffer for String {
245 fn is_mutable(&self) -> bool {
246 true
247 }
248
249 fn as_str(&self) -> &str {
250 self.as_ref()
251 }
252
253 fn insert_text(&mut self, text: &str, char_index: CharIndex) -> usize {
254 let byte_idx = byte_index_from_char_index(self.as_str(), char_index);
256
257 self.insert_str(byte_idx.into(), text);
259
260 text.chars().count()
261 }
262
263 fn delete_char_range(&mut self, char_range: Range<CharIndex>) {
264 assert!(
265 char_range.start <= char_range.end,
266 "start must be <= end, but got {char_range:?}"
267 );
268
269 let byte_start = byte_index_from_char_index(self.as_str(), char_range.start);
271 let byte_end = byte_index_from_char_index(self.as_str(), char_range.end);
272
273 self.drain((byte_start..byte_end).as_usize());
275 }
276
277 fn clear(&mut self) {
278 self.clear();
279 }
280
281 fn replace_with(&mut self, text: &str) {
282 text.clone_into(self);
283 }
284
285 fn take(&mut self) -> String {
286 core::mem::take(self)
287 }
288
289 fn type_id(&self) -> core::any::TypeId {
290 core::any::TypeId::of::<Self>()
291 }
292}
293
294impl TextBuffer for Cow<'_, str> {
295 fn is_mutable(&self) -> bool {
296 true
297 }
298
299 fn as_str(&self) -> &str {
300 self.as_ref()
301 }
302
303 fn insert_text(&mut self, text: &str, char_index: CharIndex) -> usize {
304 <String as TextBuffer>::insert_text(self.to_mut(), text, char_index)
305 }
306
307 fn delete_char_range(&mut self, char_range: Range<CharIndex>) {
308 <String as TextBuffer>::delete_char_range(self.to_mut(), char_range);
309 }
310
311 fn clear(&mut self) {
312 <String as TextBuffer>::clear(self.to_mut());
313 }
314
315 fn replace_with(&mut self, text: &str) {
316 *self = Cow::Owned(text.to_owned());
317 }
318
319 fn take(&mut self) -> String {
320 core::mem::take(self).into_owned()
321 }
322
323 fn type_id(&self) -> core::any::TypeId {
324 core::any::TypeId::of::<Cow<'_, str>>()
325 }
326}
327
328impl TextBuffer for &str {
330 fn is_mutable(&self) -> bool {
331 false
332 }
333
334 fn as_str(&self) -> &str {
335 self
336 }
337
338 fn insert_text(&mut self, _text: &str, _ch_idx: CharIndex) -> usize {
339 0
340 }
341
342 fn delete_char_range(&mut self, _ch_range: Range<CharIndex>) {}
343
344 fn type_id(&self) -> core::any::TypeId {
345 core::any::TypeId::of::<&str>()
346 }
347}
348
349#[cfg(test)]
350mod tests {
351 use super::*;
352
353 fn txt_n_sel(input: &str) -> (String, CCursorRange) {
354 assert!(
355 input.matches('[').count() == 1 && input.matches(']').count() == 1,
356 "`input` must contain exactly one `[` and one `]` to indicate the selection (cursor range)"
357 );
358 let mut primary_index = input.chars().position(|c| c == ']').unwrap();
359 let mut secondary_index = input.chars().position(|c| c == '[').unwrap();
360 let text = input.replace(['[', ']'], "");
361 if primary_index > secondary_index {
362 primary_index -= 1;
363 } else {
364 secondary_index -= 1;
365 }
366 let cursor_range = CCursorRange {
367 primary: CCursor::new(primary_index),
368 secondary: CCursor::new(secondary_index),
369 h_pos: None,
370 };
371 (text, cursor_range)
372 }
373
374 #[test]
375 fn test_txt_n_sel() {
376 assert_eq!(
377 txt_n_sel("<<L[]R>>"),
378 ("<<LR>>".to_owned(), CCursorRange::one(CCursor::new(3)))
379 );
380 assert_eq!(
381 txt_n_sel("<<L[_]R>>"),
382 (
383 "<<L_R>>".to_owned(),
384 CCursorRange::two(CCursor::new(3), CCursor::new(4))
385 )
386 );
387 assert_eq!(
388 txt_n_sel("<<左[_]右>>"),
389 (
390 "<<左_右>>".to_owned(),
391 CCursorRange::two(CCursor::new(3), CCursor::new(4))
392 )
393 );
394 assert_eq!(
395 txt_n_sel("<<L]_[R>>"),
396 (
397 "<<L_R>>".to_owned(),
398 CCursorRange::two(CCursor::new(4), CCursor::new(3))
399 )
400 );
401 }
402
403 #[test]
404 fn test_delete_surrounding_chars() {
405 fn test_case(
406 (mut input_text, input_cursor_range): (String, CCursorRange),
407 before_chars: usize,
408 after_chars: usize,
409 (expected_text, expected_cursor_range): (String, CCursorRange),
410 ) {
411 let new_cursor_range =
412 input_text.delete_surrounding_chars(input_cursor_range, before_chars, after_chars);
413 assert_eq!(input_text, expected_text);
414 assert_eq!(new_cursor_range, expected_cursor_range);
415 }
416
417 test_case(txt_n_sel("<<L[]R>>"), 1, 1, txt_n_sel("<<[]>>"));
419 test_case(txt_n_sel("<<L[_]R>>"), 1, 0, txt_n_sel("<<[_]R>>"));
420 test_case(txt_n_sel("<<L[_]R>>"), 0, 1, txt_n_sel("<<L[_]>>"));
421 test_case(txt_n_sel("<<L[_]R>>"), 1, 1, txt_n_sel("<<[_]>>"));
422 test_case(txt_n_sel("<<L[__]R>>"), 1, 1, txt_n_sel("<<[__]>>"));
423 test_case(txt_n_sel("<<LL[_]RR>>"), 2, 2, txt_n_sel("<<[_]>>"));
424 test_case(txt_n_sel("<<L]_[R>>"), 1, 0, txt_n_sel("<<]_[R>>"));
425 test_case(txt_n_sel("<<L]_[R>>"), 0, 1, txt_n_sel("<<L]_[>>"));
426 test_case(txt_n_sel("<<L]_[R>>"), 1, 1, txt_n_sel("<<]_[>>"));
427
428 test_case(txt_n_sel("<<˻[]˼>>"), 1, 1, txt_n_sel("<<[]>>"));
430 test_case(txt_n_sel("<<˻[_]˼>>"), 1, 0, txt_n_sel("<<[_]˼>>"));
431 test_case(txt_n_sel("<<˻[_]˼>>"), 0, 1, txt_n_sel("<<˻[_]>>"));
432 test_case(txt_n_sel("<<˻[_]˼>>"), 1, 1, txt_n_sel("<<[_]>>"));
433 test_case(txt_n_sel("<<˻[__]˼>>"), 1, 1, txt_n_sel("<<[__]>>"));
434 test_case(txt_n_sel("<<˻˻[_]˼˼>>"), 2, 2, txt_n_sel("<<[_]>>"));
435 test_case(txt_n_sel("<<˻]_[˼>>"), 1, 0, txt_n_sel("<<]_[˼>>"));
436 test_case(txt_n_sel("<<˻]_[˼>>"), 0, 1, txt_n_sel("<<˻]_[>>"));
437 test_case(txt_n_sel("<<˻]_[˼>>"), 1, 1, txt_n_sel("<<]_[>>"));
438
439 test_case(txt_n_sel("<<左[]右>>"), 1, 1, txt_n_sel("<<[]>>"));
441 test_case(txt_n_sel("<<左[_]右>>"), 1, 0, txt_n_sel("<<[_]右>>"));
442 test_case(txt_n_sel("<<左[_]右>>"), 0, 1, txt_n_sel("<<左[_]>>"));
443 test_case(txt_n_sel("<<左[_]右>>"), 1, 1, txt_n_sel("<<[_]>>"));
444 test_case(txt_n_sel("<<左[__]右>>"), 1, 1, txt_n_sel("<<[__]>>"));
445 test_case(txt_n_sel("<<左左[_]右右>>"), 2, 2, txt_n_sel("<<[_]>>"));
446 test_case(txt_n_sel("<<左]_[右>>"), 1, 0, txt_n_sel("<<]_[右>>"));
447 test_case(txt_n_sel("<<左]_[右>>"), 0, 1, txt_n_sel("<<左]_[>>"));
448 test_case(txt_n_sel("<<左]_[右>>"), 1, 1, txt_n_sel("<<]_[>>"));
449
450 test_case(txt_n_sel("<<L˻左[_]R˼右>>"), 3, 3, txt_n_sel("<<[_]>>"));
452 }
453}