text: Store fragment's deletions on stack (#49726)

Marco Mihai Condrache created

Helps #38927

- In the common case, each fragment has at most one deletion, so we can
skip the heap allocation
- SmallVec is smaller than an empty HashSet
- Makes cloning cheaper
- From my analysis of the code it's not possible to have duplicate ticks
(I may be wrong tho)

Before you mark this PR as ready for review, make sure that you have:
- [ ] Added a solid test coverage and/or screenshots from doing manual
testing
- [x] Done a self-review taking into account security and performance
aspects
- [ ] Aligned any UI changes with the [UI
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)

Release Notes:

- N/A

Change summary

crates/text/src/text.rs | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)

Detailed changes

crates/text/src/text.rs 🔗

@@ -23,6 +23,7 @@ use postage::{oneshot, prelude::*};
 use regex::Regex;
 pub use rope::*;
 pub use selection::*;
+use smallvec::SmallVec;
 use std::{
     borrow::Cow,
     cmp::{self, Ordering, Reverse},
@@ -541,7 +542,7 @@ pub struct Fragment {
     pub insertion_offset: usize,
     pub len: usize,
     pub visible: bool,
-    pub deletions: HashSet<clock::Lamport>,
+    pub deletions: SmallVec<[clock::Lamport; 2]>,
     pub max_undos: clock::Global,
 }
 
@@ -948,7 +949,7 @@ impl Buffer {
                     intersection.insertion_offset += fragment_start - old_fragments.start().visible;
                     intersection.id =
                         Locator::between(&new_fragments.summary().max_id, &intersection.id);
-                    intersection.deletions.insert(timestamp);
+                    intersection.deletions.push(timestamp);
                     intersection.visible = false;
                 }
                 if intersection.len > 0 {
@@ -1197,7 +1198,7 @@ impl Buffer {
                         fragment_start - old_fragments.start().0.full_offset();
                     intersection.id =
                         Locator::between(&new_fragments.summary().max_id, &intersection.id);
-                    intersection.deletions.insert(timestamp);
+                    intersection.deletions.push(timestamp);
                     intersection.visible = false;
                     insertion_slices.push(InsertionSlice::from_fragment(timestamp, &intersection));
                 }