1use std::borrow::{Borrow, Cow};
16use std::cmp::min;
17use std::collections::BTreeSet;
18
19use serde_json::Value;
20
21use xi_rope::diff::{Diff, LineHashDiff};
22use xi_rope::engine::{Engine, RevId, RevToken};
23use xi_rope::rope::count_newlines;
24use xi_rope::spans::SpansBuilder;
25use xi_rope::{Cursor, DeltaBuilder, Interval, LinesMetric, Rope, RopeDelta, Transformer};
26use xi_trace::{trace_block, trace_payload};
27
28use crate::annotations::{AnnotationType, Annotations};
29use crate::config::BufferItems;
30use crate::edit_types::BufferEvent;
31use crate::event_context::MAX_SIZE_LIMIT;
32use crate::layers::Layers;
33use crate::movement::{region_movement, Movement};
34use crate::plugins::rpc::{DataSpan, GetDataResponse, PluginEdit, ScopeSpan, TextUnit};
35use crate::plugins::PluginId;
36use crate::rpc::SelectionModifier;
37use crate::selection::{InsertDrift, SelRegion, Selection};
38use crate::styles::ThemeStyleMap;
39use crate::view::{Replace, View};
40use crate::word_boundaries::WordCursor;
41
42#[cfg(not(feature = "ledger"))]
43pub struct SyncStore;
44use crate::backspace::offset_for_delete_backwards;
45#[cfg(feature = "ledger")]
46use fuchsia::sync::SyncStore;
47
48const MAX_UNDOS: usize = 20;
51
52enum IndentDirection {
53 In,
54 Out,
55}
56
57pub struct Editor {
58 text: Rope,
60 engine: Engine,
62
63 last_rev_id: RevId,
65 pristine_rev_id: RevId,
67 undo_group_id: usize,
68 live_undos: Vec<usize>,
70 cur_undo: usize,
73 undos: BTreeSet<usize>,
75 gc_undos: BTreeSet<usize>,
77 force_undo_group: bool,
78
79 this_edit_type: EditType,
80 last_edit_type: EditType,
81
82 revs_in_flight: usize,
83
84 #[allow(dead_code)]
86 sync_store: Option<SyncStore>,
87 #[allow(dead_code)]
88 last_synced_rev: RevId,
89
90 layers: Layers,
91}
92
93impl Editor {
94 pub fn new() -> Editor {
96 Self::with_text("")
97 }
98
99 pub fn with_text<T: Into<Rope>>(text: T) -> Editor {
101 let engine = Engine::new(text.into());
102 let buffer = engine.get_head().clone();
103 let last_rev_id = engine.get_head_rev_id();
104
105 Editor {
106 text: buffer,
107 engine,
108 last_rev_id,
109 pristine_rev_id: last_rev_id,
110 undo_group_id: 1,
111 live_undos: vec![0],
115 cur_undo: 1,
116 undos: BTreeSet::new(),
117 gc_undos: BTreeSet::new(),
118 force_undo_group: false,
119 last_edit_type: EditType::Other,
120 this_edit_type: EditType::Other,
121 layers: Layers::default(),
122 revs_in_flight: 0,
123 sync_store: None,
124 last_synced_rev: last_rev_id,
125 }
126 }
127
128 pub(crate) fn get_buffer(&self) -> &Rope {
129 &self.text
130 }
131
132 pub(crate) fn get_layers(&self) -> &Layers {
133 &self.layers
134 }
135
136 pub(crate) fn get_layers_mut(&mut self) -> &mut Layers {
137 &mut self.layers
138 }
139
140 pub(crate) fn get_head_rev_token(&self) -> u64 {
141 self.engine.get_head_rev_id().token()
142 }
143
144 pub(crate) fn get_edit_type(&self) -> EditType {
145 self.this_edit_type
146 }
147
148 pub(crate) fn get_active_undo_group(&self) -> usize {
149 *self.live_undos.last().unwrap_or(&0)
150 }
151
152 pub(crate) fn update_edit_type(&mut self) {
153 self.last_edit_type = self.this_edit_type;
154 self.this_edit_type = EditType::Other
155 }
156
157 pub(crate) fn set_pristine(&mut self) {
158 self.pristine_rev_id = self.engine.get_head_rev_id();
159 }
160
161 pub(crate) fn is_pristine(&self) -> bool {
162 self.engine.is_equivalent_revision(self.pristine_rev_id, self.engine.get_head_rev_id())
163 }
164
165 pub(crate) fn set_force_undo_group(&mut self, force_undo_group: bool) {
171 trace_payload("Editor::set_force_undo_group", &["core"], force_undo_group.to_string());
172 self.force_undo_group = force_undo_group;
173 }
174
175 pub fn reload(&mut self, text: Rope) {
178 let delta = LineHashDiff::compute_delta(self.get_buffer(), &text);
179 self.add_delta(delta);
180 self.set_pristine();
181 }
182
183 pub fn increment_revs_in_flight(&mut self) {
185 self.revs_in_flight += 1;
186 }
187
188 pub fn dec_revs_in_flight(&mut self) {
191 self.revs_in_flight -= 1;
192 self.gc_undos();
193 }
194
195 fn insert<T: Into<Rope>>(&mut self, view: &View, text: T) {
196 let rope = text.into();
197 let mut builder = DeltaBuilder::new(self.text.len());
198 for region in view.sel_regions() {
199 let iv = Interval::new(region.min(), region.max());
200 builder.replace(iv, rope.clone());
201 }
202 self.add_delta(builder.build());
203 }
204
205 fn surround<BT, AT>(&mut self, view: &View, before_text: BT, after_text: AT)
207 where
208 BT: Into<Rope>,
209 AT: Into<Rope>,
210 {
211 let mut builder = DeltaBuilder::new(self.text.len());
212 let before_rope = before_text.into();
213 let after_rope = after_text.into();
214 for region in view.sel_regions() {
215 let before_iv = Interval::new(region.min(), region.min());
216 builder.replace(before_iv, before_rope.clone());
217 let after_iv = Interval::new(region.max(), region.max());
218 builder.replace(after_iv, after_rope.clone());
219 }
220 self.add_delta(builder.build());
221 }
222
223 fn add_delta(&mut self, delta: RopeDelta) {
235 let head_rev_id = self.engine.get_head_rev_id();
236 let undo_group = self.calculate_undo_group();
237 self.last_edit_type = self.this_edit_type;
238 let priority = 0x10000;
239 self.engine.edit_rev(priority, undo_group, head_rev_id.token(), delta);
240 self.text = self.engine.get_head().clone();
241 }
242
243 pub(crate) fn calculate_undo_group(&mut self) -> usize {
244 let has_undos = !self.live_undos.is_empty();
245 let force_undo_group = self.force_undo_group;
246 let is_unbroken_group = !self.this_edit_type.breaks_undo_group(self.last_edit_type);
247
248 if has_undos && (force_undo_group || is_unbroken_group) {
249 *self.live_undos.last().unwrap()
250 } else {
251 let undo_group = self.undo_group_id;
252 self.gc_undos.extend(&self.live_undos[self.cur_undo..]);
253 self.live_undos.truncate(self.cur_undo);
254 self.live_undos.push(undo_group);
255 if self.live_undos.len() <= MAX_UNDOS {
256 self.cur_undo += 1;
257 } else {
258 self.gc_undos.insert(self.live_undos.remove(0));
259 }
260 self.undo_group_id += 1;
261 undo_group
262 }
263 }
264
265 pub fn apply_plugin_edit(&mut self, edit: PluginEdit) {
267 let _t = trace_block("Editor::apply_plugin_edit", &["core"]);
268 let PluginEdit { rev, delta, priority, undo_group, .. } = edit;
270 let priority = priority as usize;
271 let undo_group = undo_group.unwrap_or_else(|| self.calculate_undo_group());
272 match self.engine.try_edit_rev(priority, undo_group, rev, delta) {
273 Err(e) => error!("Error applying plugin edit: {}", e),
274 Ok(_) => self.text = self.engine.get_head().clone(),
275 };
276 }
277
278 pub(crate) fn commit_delta(&mut self) -> Option<(RopeDelta, Rope, InsertDrift)> {
283 let _t = trace_block("Editor::commit_delta", &["core"]);
284
285 if self.engine.get_head_rev_id() == self.last_rev_id {
286 return None;
287 }
288
289 let last_token = self.last_rev_id.token();
290 let delta = self.engine.try_delta_rev_head(last_token).expect("last_rev not found");
291 let last_text = self.engine.get_rev(last_token).expect("last_rev not found");
294
295 let drift = match self.this_edit_type {
298 EditType::Transpose => InsertDrift::Inside,
299 EditType::Surround => InsertDrift::Outside,
300 _ => InsertDrift::Default,
301 };
302 self.layers.update_all(&delta);
303
304 self.last_rev_id = self.engine.get_head_rev_id();
305 self.sync_state_changed();
306 Some((delta, last_text, drift))
307 }
308
309 pub(crate) fn delta_rev_head(&self, target_rev_id: RevToken) -> Option<RopeDelta> {
313 self.engine.try_delta_rev_head(target_rev_id).ok()
314 }
315
316 #[cfg(not(target_os = "fuchsia"))]
317 fn gc_undos(&mut self) {
318 if self.revs_in_flight == 0 && !self.gc_undos.is_empty() {
319 self.engine.gc(&self.gc_undos);
320 self.undos = &self.undos - &self.gc_undos;
321 self.gc_undos.clear();
322 }
323 }
324
325 #[cfg(target_os = "fuchsia")]
326 fn gc_undos(&mut self) {
327 }
330
331 pub fn merge_new_state(&mut self, new_engine: Engine) {
332 self.engine.merge(&new_engine);
333 self.text = self.engine.get_head().clone();
334 self.undo_group_id = self.engine.max_undo_group_id() + 1;
337 self.last_synced_rev = self.engine.get_head_rev_id();
338 self.commit_delta();
339 }
342
343 pub fn set_session_id(&mut self, session: (u64, u32)) {
345 self.engine.set_session_id(session);
346 }
347
348 #[cfg(feature = "ledger")]
349 pub fn set_sync_store(&mut self, sync_store: SyncStore) {
350 self.sync_store = Some(sync_store);
351 }
352
353 #[cfg(not(feature = "ledger"))]
354 pub fn sync_state_changed(&mut self) {}
355
356 #[cfg(feature = "ledger")]
357 pub fn sync_state_changed(&mut self) {
358 if let Some(sync_store) = self.sync_store.as_mut() {
359 if self.last_synced_rev != self.engine.get_head_rev_id() {
361 self.last_synced_rev = self.engine.get_head_rev_id();
362 sync_store.state_changed();
363 }
364 }
365 }
366
367 #[cfg(feature = "ledger")]
368 pub fn transaction_ready(&mut self) {
369 if let Some(sync_store) = self.sync_store.as_mut() {
370 sync_store.commit_transaction(&self.engine);
371 }
372 }
373
374 fn delete_backward(&mut self, view: &View, config: &BufferItems) {
375 let mut builder = DeltaBuilder::new(self.text.len());
378 for region in view.sel_regions() {
379 let start = offset_for_delete_backwards(&view, ®ion, &self.text, &config);
380 let iv = Interval::new(start, region.max());
381 if !iv.is_empty() {
382 builder.delete(iv);
383 }
384 }
385
386 if !builder.is_empty() {
387 self.this_edit_type = EditType::Delete;
388 self.add_delta(builder.build());
389 }
390 }
391
392 fn delete_by_movement(
399 &mut self,
400 view: &View,
401 movement: Movement,
402 save: bool,
403 kill_ring: &mut Rope,
404 ) {
405 let mut deletions = Selection::new();
409 for &r in view.sel_regions() {
410 if r.is_caret() {
411 let new_region = region_movement(movement, r, view, &self.text, true);
412 deletions.add_region(new_region);
413 } else {
414 deletions.add_region(r);
415 }
416 }
417 if save {
418 let saved = self.extract_sel_regions(&deletions).unwrap_or_default();
419 *kill_ring = Rope::from(saved);
420 }
421 self.delete_sel_regions(&deletions);
422 }
423
424 fn delete_sel_regions(&mut self, sel_regions: &[SelRegion]) {
426 let mut builder = DeltaBuilder::new(self.text.len());
427 for region in sel_regions {
428 let iv = Interval::new(region.min(), region.max());
429 if !iv.is_empty() {
430 builder.delete(iv);
431 }
432 }
433 if !builder.is_empty() {
434 self.this_edit_type = EditType::Delete;
435 self.add_delta(builder.build());
436 }
437 }
438
439 fn extract_sel_regions(&self, sel_regions: &[SelRegion]) -> Option<Cow<str>> {
442 let mut saved = None;
443 for region in sel_regions {
444 if !region.is_caret() {
445 let val = self.text.slice_to_cow(region);
446 match saved {
447 None => saved = Some(val),
448 Some(ref mut s) => {
449 s.to_mut().push('\n');
450 s.to_mut().push_str(&val);
451 }
452 }
453 }
454 }
455 saved
456 }
457
458 fn insert_newline(&mut self, view: &View, config: &BufferItems) {
459 self.this_edit_type = EditType::InsertNewline;
460 self.insert(view, &config.line_ending);
461 }
462
463 fn insert_tab(&mut self, view: &View, config: &BufferItems) {
464 self.this_edit_type = EditType::InsertChars;
465 let mut builder = DeltaBuilder::new(self.text.len());
466 let const_tab_text = self.get_tab_text(config, None);
467
468 if view.sel_regions().len() > 1 {
469 self.this_edit_type = EditType::Indent;
473 }
474
475 for region in view.sel_regions() {
476 let line_range = view.get_line_range(&self.text, region);
477
478 if line_range.len() > 1 {
479 self.this_edit_type = EditType::Indent;
480 for line in line_range {
481 let offset = view.line_col_to_offset(&self.text, line, 0);
482 let iv = Interval::new(offset, offset);
483 builder.replace(iv, Rope::from(const_tab_text));
484 }
485 } else {
486 let (_, col) = view.offset_to_line_col(&self.text, region.start);
487 let mut tab_size = config.tab_size;
488 tab_size = tab_size - (col % tab_size);
489 let tab_text = self.get_tab_text(config, Some(tab_size));
490
491 let iv = Interval::new(region.min(), region.max());
492 builder.replace(iv, Rope::from(tab_text));
493 }
494 }
495 self.add_delta(builder.build());
496 }
497
498 fn modify_indent(&mut self, view: &View, config: &BufferItems, direction: IndentDirection) {
504 self.this_edit_type = EditType::Indent;
505 let mut lines = BTreeSet::new();
506 let tab_text = self.get_tab_text(config, None);
507 for region in view.sel_regions() {
508 let line_range = view.get_line_range(&self.text, region);
509 for line in line_range {
510 lines.insert(line);
511 }
512 }
513 match direction {
514 IndentDirection::In => self.indent(view, lines, tab_text),
515 IndentDirection::Out => self.outdent(view, lines, tab_text),
516 };
517 }
518
519 fn indent(&mut self, view: &View, lines: BTreeSet<usize>, tab_text: &str) {
520 let mut builder = DeltaBuilder::new(self.text.len());
521 for line in lines {
522 let offset = view.line_col_to_offset(&self.text, line, 0);
523 let interval = Interval::new(offset, offset);
524 builder.replace(interval, Rope::from(tab_text));
525 }
526 self.this_edit_type = EditType::InsertChars;
527 self.add_delta(builder.build());
528 }
529
530 fn outdent(&mut self, view: &View, lines: BTreeSet<usize>, tab_text: &str) {
531 let mut builder = DeltaBuilder::new(self.text.len());
532 for line in lines {
533 let offset = view.line_col_to_offset(&self.text, line, 0);
534 let tab_offset = view.line_col_to_offset(&self.text, line, tab_text.len());
535 let interval = Interval::new(offset, tab_offset);
536 let leading_slice = self.text.slice_to_cow(interval.start()..interval.end());
537 if leading_slice == tab_text {
538 builder.delete(interval);
539 } else if let Some(first_char_col) = leading_slice.find(|c: char| !c.is_whitespace()) {
540 let first_char_offset = view.line_col_to_offset(&self.text, line, first_char_col);
541 let interval = Interval::new(offset, first_char_offset);
542 builder.delete(interval);
543 }
544 }
545 self.this_edit_type = EditType::Delete;
546 self.add_delta(builder.build());
547 }
548
549 fn get_tab_text(&self, config: &BufferItems, tab_size: Option<usize>) -> &'static str {
550 let tab_size = tab_size.unwrap_or(config.tab_size);
551 let tab_text = if config.translate_tabs_to_spaces { n_spaces(tab_size) } else { "\t" };
552
553 tab_text
554 }
555
556 fn do_insert(&mut self, view: &View, config: &BufferItems, chars: &str) {
557 let pair_search = config.surrounding_pairs.iter().find(|pair| pair.0 == chars);
558 let caret_exists = view.sel_regions().iter().any(|region| region.is_caret());
559 if let (Some(pair), false) = (pair_search, caret_exists) {
560 self.this_edit_type = EditType::Surround;
561 self.surround(view, pair.0.to_string(), pair.1.to_string());
562 } else {
563 self.this_edit_type = EditType::InsertChars;
564 self.insert(view, chars);
565 }
566 }
567
568 fn do_paste(&mut self, view: &View, chars: &str) {
569 if view.sel_regions().len() == 1 || view.sel_regions().len() != count_lines(chars) {
570 self.insert(view, chars);
571 } else {
572 let mut builder = DeltaBuilder::new(self.text.len());
573 for (sel, line) in view.sel_regions().iter().zip(chars.lines()) {
574 let iv = Interval::new(sel.min(), sel.max());
575 builder.replace(iv, line.into());
576 }
577 self.add_delta(builder.build());
578 }
579 }
580
581 pub(crate) fn do_cut(&mut self, view: &mut View) -> Value {
582 let result = self.do_copy(view);
583 self.delete_sel_regions(&view.sel_regions());
584 result
585 }
586
587 pub(crate) fn do_copy(&self, view: &View) -> Value {
588 if let Some(val) = self.extract_sel_regions(view.sel_regions()) {
589 Value::String(val.into_owned())
590 } else {
591 Value::Null
592 }
593 }
594
595 fn do_undo(&mut self) {
596 if self.cur_undo > 1 {
597 self.cur_undo -= 1;
598 assert!(self.undos.insert(self.live_undos[self.cur_undo]));
599 self.this_edit_type = EditType::Undo;
600 self.update_undos();
601 }
602 }
603
604 fn do_redo(&mut self) {
605 if self.cur_undo < self.live_undos.len() {
606 assert!(self.undos.remove(&self.live_undos[self.cur_undo]));
607 self.cur_undo += 1;
608 self.this_edit_type = EditType::Redo;
609 self.update_undos();
610 }
611 }
612
613 fn update_undos(&mut self) {
614 self.engine.undo(self.undos.clone());
615 self.text = self.engine.get_head().clone();
616 }
617
618 fn sel_region_to_interval_and_rope(&self, region: SelRegion) -> (Interval, Rope) {
619 let as_interval = Interval::new(region.min(), region.max());
620 let interval_rope = self.text.subseq(as_interval);
621 (as_interval, interval_rope)
622 }
623
624 fn do_transpose(&mut self, view: &View) {
625 let mut builder = DeltaBuilder::new(self.text.len());
626 let mut last = 0;
627 let mut optional_previous_selection: Option<(Interval, Rope)> =
628 last_selection_region(view.sel_regions())
629 .map(|®ion| self.sel_region_to_interval_and_rope(region));
630
631 for ®ion in view.sel_regions() {
632 if region.is_caret() {
633 let mut middle = region.end;
634 let mut start = self.text.prev_grapheme_offset(middle).unwrap_or(0);
635 let mut end = self.text.next_grapheme_offset(middle).unwrap_or(middle);
636
637 if start >= last {
640 let end_line_offset =
641 view.offset_of_line(&self.text, view.line_of_offset(&self.text, end));
642 if (end == middle || end == end_line_offset) && end != self.text.len() {
644 middle = start;
645 start = self.text.prev_grapheme_offset(middle).unwrap_or(0);
646 end = middle.wrapping_add(1);
647 }
648
649 let interval = Interval::new(start, end);
650 let before = self.text.slice_to_cow(start..middle);
651 let after = self.text.slice_to_cow(middle..end);
652 let swapped: String = [after, before].concat();
653 builder.replace(interval, Rope::from(swapped));
654 last = end;
655 }
656 } else if let Some(previous_selection) = optional_previous_selection {
657 let current_interval = self.sel_region_to_interval_and_rope(region);
658 builder.replace(current_interval.0, previous_selection.1);
659 optional_previous_selection = Some(current_interval);
660 }
661 }
662 if !builder.is_empty() {
663 self.this_edit_type = EditType::Transpose;
664 self.add_delta(builder.build());
665 }
666 }
667
668 fn yank(&mut self, view: &View, kill_ring: &mut Rope) {
669 self.insert(view, kill_ring.clone());
673 }
674
675 fn replace(&mut self, view: &mut View, replace_all: bool) {
676 if let Some(Replace { chars, .. }) = view.get_replace() {
677 let mut old_selection = Selection::new();
680 for ®ion in view.sel_regions() {
681 old_selection.add_region(region);
682 }
683 view.collapse_selections(&self.text);
684
685 if replace_all {
686 view.do_find_all(&self.text);
687 } else {
688 view.do_find_next(&self.text, false, true, true, &SelectionModifier::Set);
689 }
690
691 match last_selection_region(view.sel_regions()) {
692 Some(_) => self.insert(view, chars),
693 None => return,
694 };
695 }
696 }
697
698 fn transform_text<F: Fn(&str) -> String>(&mut self, view: &View, transform_function: F) {
699 let mut builder = DeltaBuilder::new(self.text.len());
700
701 for region in view.sel_regions() {
702 let selected_text = self.text.slice_to_cow(region);
703 let interval = Interval::new(region.min(), region.max());
704 builder.replace(interval, Rope::from(transform_function(&selected_text)));
705 }
706 if !builder.is_empty() {
707 self.this_edit_type = EditType::Other;
708 self.add_delta(builder.build());
709 }
710 }
711
712 fn change_number<F: Fn(i128) -> Option<i128>>(&mut self, view: &View, transform_function: F) {
725 let mut builder = DeltaBuilder::new(self.text.len());
726 for region in view.sel_regions() {
727 let mut cursor = WordCursor::new(&self.text, region.end);
728 let (mut start, end) = cursor.select_word();
729
730 if start > 0 && self.text.byte_at(start - 1) == (b'-') {
732 start -= 1;
733 }
734
735 let word = self.text.slice_to_cow(start..end);
736 if let Some(number) = word.parse::<i128>().ok().and_then(&transform_function) {
737 let interval = Interval::new(start, end);
738 builder.replace(interval, Rope::from(number.to_string()));
739 }
740 }
741
742 if !builder.is_empty() {
743 self.this_edit_type = EditType::Other;
744 self.add_delta(builder.build());
745 }
746 }
747
748 fn capitalize_text(&mut self, view: &mut View) {
750 let mut builder = DeltaBuilder::new(self.text.len());
751 let mut final_selection = Selection::new();
752
753 for ®ion in view.sel_regions() {
754 final_selection.add_region(SelRegion::new(region.max(), region.max()));
755 let mut word_cursor = WordCursor::new(&self.text, region.min());
756
757 loop {
758 let (start, end) = word_cursor.select_word();
760
761 if start < end {
762 let interval = Interval::new(start, end);
763 let word = self.text.slice_to_cow(start..end);
764
765 let (first_char, rest) = word.split_at(1);
767 let capitalized_text =
768 [first_char.to_uppercase(), rest.to_lowercase()].concat();
769 builder.replace(interval, Rope::from(capitalized_text));
770 }
771
772 if word_cursor.next_boundary().is_none() || end > region.max() {
773 break;
774 }
775 }
776 }
777
778 if !builder.is_empty() {
779 self.this_edit_type = EditType::Other;
780 self.add_delta(builder.build());
781 }
782
783 view.collapse_selections(&self.text);
786 view.set_selection(&self.text, final_selection);
787 }
788
789 fn duplicate_line(&mut self, view: &View, config: &BufferItems) {
790 let mut builder = DeltaBuilder::new(self.text.len());
791 let mut to_duplicate = BTreeSet::new();
793
794 for region in view.sel_regions() {
795 let (first_line, _) = view.offset_to_line_col(&self.text, region.min());
796 let line_start = view.offset_of_line(&self.text, first_line);
797
798 let mut cursor = match region.is_caret() {
799 true => Cursor::new(&self.text, line_start),
800 false => {
801 let (last_line, _) = view.offset_to_line_col(&self.text, region.max());
803 let line_end = view.offset_of_line(&self.text, last_line);
804 Cursor::new(&self.text, line_end)
805 }
806 };
807
808 if let Some(line_end) = cursor.next::<LinesMetric>() {
809 to_duplicate.insert((line_start, line_end));
810 }
811 }
812
813 for (start, end) in to_duplicate {
814 let iv = Interval::new(start, start);
816 builder.replace(iv, self.text.slice(start..end));
817
818 if end == self.text.len() {
820 builder.replace(iv, Rope::from(&config.line_ending))
821 }
822 }
823
824 self.this_edit_type = EditType::Other;
825 self.add_delta(builder.build());
826 }
827
828 pub(crate) fn do_edit(
829 &mut self,
830 view: &mut View,
831 kill_ring: &mut Rope,
832 config: &BufferItems,
833 cmd: BufferEvent,
834 ) {
835 use self::BufferEvent::*;
836 match cmd {
837 Delete { movement, kill } => self.delete_by_movement(view, movement, kill, kill_ring),
838 Backspace => self.delete_backward(view, config),
839 Transpose => self.do_transpose(view),
840 Undo => self.do_undo(),
841 Redo => self.do_redo(),
842 Uppercase => self.transform_text(view, |s| s.to_uppercase()),
843 Lowercase => self.transform_text(view, |s| s.to_lowercase()),
844 Capitalize => self.capitalize_text(view),
845 Indent => self.modify_indent(view, config, IndentDirection::In),
846 Outdent => self.modify_indent(view, config, IndentDirection::Out),
847 InsertNewline => self.insert_newline(view, config),
848 InsertTab => self.insert_tab(view, config),
849 Insert(chars) => self.do_insert(view, config, &chars),
850 Paste(chars) => self.do_paste(view, &chars),
851 Yank => self.yank(view, kill_ring),
852 ReplaceNext => self.replace(view, false),
853 ReplaceAll => self.replace(view, true),
854 DuplicateLine => self.duplicate_line(view, config),
855 IncreaseNumber => self.change_number(view, |s| s.checked_add(1)),
856 DecreaseNumber => self.change_number(view, |s| s.checked_sub(1)),
857 }
858 }
859
860 pub fn theme_changed(&mut self, style_map: &ThemeStyleMap) {
861 self.layers.theme_changed(style_map);
862 }
863
864 pub fn plugin_n_lines(&self) -> usize {
865 self.text.measure::<LinesMetric>() + 1
866 }
867
868 pub fn update_spans(
869 &mut self,
870 view: &mut View,
871 plugin: PluginId,
872 start: usize,
873 len: usize,
874 spans: Vec<ScopeSpan>,
875 rev: RevToken,
876 ) {
877 let _t = trace_block("Editor::update_spans", &["core"]);
878 let mut start = start;
880 let mut end_offset = start + len;
881 let mut sb = SpansBuilder::new(len);
882 for span in spans {
883 sb.add_span(Interval::new(span.start, span.end), span.scope_id);
884 }
885 let mut spans = sb.build();
886 if rev != self.engine.get_head_rev_id().token() {
887 if let Ok(delta) = self.engine.try_delta_rev_head(rev) {
888 let mut transformer = Transformer::new(&delta);
889 let new_start = transformer.transform(start, false);
890 if !transformer.interval_untouched(Interval::new(start, end_offset)) {
891 spans = spans.transform(start, end_offset, &mut transformer);
892 }
893 start = new_start;
894 end_offset = transformer.transform(end_offset, true);
895 } else {
896 error!("Revision {} not found", rev);
897 }
898 }
899 let iv = Interval::new(start, end_offset);
900 self.layers.update_layer(plugin, iv, spans);
901 view.invalidate_styles(&self.text, start, end_offset);
902 }
903
904 pub fn update_annotations(
905 &mut self,
906 view: &mut View,
907 plugin: PluginId,
908 start: usize,
909 len: usize,
910 annotation_spans: Vec<DataSpan>,
911 annotation_type: AnnotationType,
912 rev: RevToken,
913 ) {
914 let _t = trace_block("Editor::update_annotations", &["core"]);
915
916 let mut start = start;
917 let mut end_offset = start + len;
918 let mut sb = SpansBuilder::new(len);
919 for span in annotation_spans {
920 sb.add_span(Interval::new(span.start, span.end), span.data);
921 }
922 let mut spans = sb.build();
923 if rev != self.engine.get_head_rev_id().token() {
924 if let Ok(delta) = self.engine.try_delta_rev_head(rev) {
925 let mut transformer = Transformer::new(&delta);
926 let new_start = transformer.transform(start, false);
927 if !transformer.interval_untouched(Interval::new(start, end_offset)) {
928 spans = spans.transform(start, end_offset, &mut transformer);
929 }
930 start = new_start;
931 end_offset = transformer.transform(end_offset, true);
932 } else {
933 error!("Revision {} not found", rev);
934 }
935 }
936 let iv = Interval::new(start, end_offset);
937 view.update_annotations(plugin, iv, Annotations { items: spans, annotation_type });
938 }
939
940 pub(crate) fn get_rev(&self, rev: RevToken) -> Option<Cow<Rope>> {
941 let text_cow = if rev == self.engine.get_head_rev_id().token() {
942 Cow::Borrowed(&self.text)
943 } else {
944 match self.engine.get_rev(rev) {
945 None => return None,
946 Some(text) => Cow::Owned(text),
947 }
948 };
949
950 Some(text_cow)
951 }
952
953 pub fn plugin_get_data(
954 &self,
955 start: usize,
956 unit: TextUnit,
957 max_size: usize,
958 rev: RevToken,
959 ) -> Option<GetDataResponse> {
960 let _t = trace_block("Editor::plugin_get_data", &["core"]);
961 let text_cow = self.get_rev(rev)?;
962 let text = &text_cow;
963 let offset = unit.resolve_offset(text.borrow(), start)?;
965
966 let max_size = min(max_size, MAX_SIZE_LIMIT);
967 let mut end_off = offset.saturating_add(max_size);
968 if end_off >= text.len() {
969 end_off = text.len();
970 } else {
971 end_off = text.prev_codepoint_offset(end_off + 1).unwrap();
973 }
974
975 let chunk = text.slice_to_cow(offset..end_off).into_owned();
976 let first_line = text.line_of_offset(offset);
977 let first_line_offset = offset - text.offset_of_line(first_line);
978
979 Some(GetDataResponse { chunk, offset, first_line, first_line_offset })
980 }
981}
982
983#[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)]
984#[serde(rename_all = "snake_case")]
985pub enum EditType {
986 Other,
989 #[serde(rename = "insert")]
991 InsertChars,
992 #[serde(rename = "newline")]
993 InsertNewline,
994 Indent,
996 Delete,
997 Undo,
998 Redo,
999 Transpose,
1000 Surround,
1001}
1002
1003impl EditType {
1004 fn breaks_undo_group(self, previous: EditType) -> bool {
1006 self == EditType::Other || self == EditType::Transpose || self != previous
1007 }
1008}
1009
1010fn last_selection_region(regions: &[SelRegion]) -> Option<&SelRegion> {
1011 for region in regions.iter().rev() {
1012 if !region.is_caret() {
1013 return Some(region);
1014 }
1015 }
1016 None
1017}
1018
1019fn n_spaces(n: usize) -> &'static str {
1020 let spaces = " ";
1021 assert!(n <= spaces.len());
1022 &spaces[..n]
1023}
1024
1025fn count_lines(s: &str) -> usize {
1027 let mut newlines = count_newlines(s);
1028 if s.as_bytes().last() == Some(&0xa) {
1029 newlines -= 1;
1030 }
1031 1 + newlines
1032}
1033
1034#[cfg(test)]
1035mod tests {
1036 use super::*;
1037
1038 #[test]
1039 fn plugin_edit() {
1040 let base_text = "hello";
1041 let mut editor = Editor::with_text(base_text);
1042 let mut builder = DeltaBuilder::new(base_text.len());
1043 builder.replace(0..0, "s".into());
1044 let delta = builder.build();
1045 let rev = editor.get_head_rev_token();
1046
1047 let edit_one = PluginEdit {
1048 rev,
1049 delta,
1050 priority: 55,
1051 after_cursor: false,
1052 undo_group: None,
1053 author: "plugin_one".into(),
1054 };
1055
1056 editor.apply_plugin_edit(edit_one.clone());
1057 editor.apply_plugin_edit(edit_one);
1058
1059 assert_eq!(editor.get_buffer().to_string(), "sshello");
1060 }
1061
1062}