rework trailing newline handling, get to SEEd=97

Cole Miller created

Change summary

crates/multi_buffer/src/multi_buffer.rs | 24 ++++++++--
crates/multi_buffer/src/path_key.rs     | 61 ++++++++++++++++++++++----
2 files changed, 69 insertions(+), 16 deletions(-)

Detailed changes

crates/multi_buffer/src/multi_buffer.rs 🔗

@@ -6507,33 +6507,47 @@ impl MultiBufferSnapshot {
                 let prev = &excerpts[ix - 1];
 
                 if excerpt.path_key < prev.path_key {
-                    panic!("excerpt path_keys are out-of-order: {:?}", excerpts);
+                    panic!("excerpt path_keys are out-of-order: {:#?}", excerpts);
                 } else if excerpt.path_key == prev.path_key {
                     if excerpt
                         .start_anchor()
                         .cmp(&prev.end_anchor(), &self)
                         .is_le()
                     {
-                        panic!("excerpt anchors are out-of-order: {:?}", excerpts);
+                        panic!("excerpt anchors are out-of-order: {:#?}", excerpts);
                     }
                     if excerpt
                         .start_anchor()
                         .cmp(&excerpt.end_anchor(), &self)
                         .is_ge()
                     {
-                        panic!("excerpt with backward range: {:?}", excerpts);
+                        panic!("excerpt with backward range: {:#?}", excerpts);
                     }
                 }
             }
+
+            if ix < excerpts.len() - 1 {
+                assert!(
+                    excerpt.has_trailing_newline,
+                    "non-trailing excerpt has no trailing newline: {:#?}",
+                    excerpts
+                );
+            } else {
+                assert!(
+                    !excerpt.has_trailing_newline,
+                    "trailing excerpt has trailing newline: {:#?}",
+                    excerpts
+                );
+            }
             assert!(
                 all_buffer_path_keys.contains(&excerpt.path_key),
-                "excerpt path key not found in active path keys: {:?}",
+                "excerpt path key not found in active path keys: {:#?}",
                 excerpt.path_key
             );
             assert_eq!(
                 self.path_keys_by_index.get(&excerpt.path_key_index),
                 Some(&excerpt.path_key),
-                "excerpt path key index does not match path key: {:?}",
+                "excerpt path key index does not match path key: {:#?}",
                 excerpt.path_key,
             );
         }

crates/multi_buffer/src/path_key.rs 🔗

@@ -12,7 +12,7 @@ use ztracing::instrument;
 use crate::{
     Anchor, BufferState, BufferStateSnapshot, DiffChangeKind, Event, Excerpt, ExcerptOffset,
     ExcerptRange, ExcerptSummary, ExpandExcerptDirection, MultiBuffer, MultiBufferDimension,
-    PathKeyIndex, ToOffset, build_excerpt_ranges,
+    MultiBufferOffset, PathKeyIndex, ToOffset, build_excerpt_ranges,
 };
 
 #[derive(PartialEq, Eq, Ord, PartialOrd, Clone, Hash, Debug)]
@@ -323,6 +323,7 @@ impl MultiBuffer {
         to_insert: &Vec<ExcerptRange<text::Anchor>>,
         cx: &mut Context<Self>,
     ) -> (bool, PathKeyIndex) {
+        dbg!(&path_key, &to_insert);
         let path_key_index = self.get_or_create_path_key_index(&path_key);
         if let Some(old_path_key) = self
             .snapshot(cx)
@@ -378,6 +379,19 @@ impl MultiBuffer {
                 break;
             };
             if &excerpt.range == *next_excerpt {
+                let before = new_excerpts.summary().len();
+                new_excerpts.update_last(
+                    |prev_excerpt| {
+                        if !prev_excerpt.has_trailing_newline {
+                            prev_excerpt.has_trailing_newline = true;
+                            patch.push(Edit {
+                                old: cursor.position.1..cursor.position.1,
+                                new: before..before + MultiBufferOffset(1),
+                            });
+                        }
+                    },
+                    (),
+                );
                 new_excerpts.push(excerpt.clone(), ());
                 to_insert.next();
                 cursor.next();
@@ -404,17 +418,20 @@ impl MultiBuffer {
                 let next_excerpt = to_insert.next().unwrap();
                 added_new_excerpt = true;
                 let before = new_excerpts.summary().len();
-                new_excerpts.update_last(|excerpt| excerpt.has_trailing_newline = true, ());
+                new_excerpts.update_last(
+                    |prev_excerpt| {
+                        dbg!("NORMAL INSERT");
+                        prev_excerpt.has_trailing_newline = true;
+                    },
+                    (),
+                );
                 new_excerpts.push(
                     Excerpt::new(
                         path_key.clone(),
                         path_key_index,
                         &buffer_snapshot,
                         next_excerpt.clone(),
-                        to_insert.peek().is_some()
-                            || cursor
-                                .next_item()
-                                .is_some_and(|item| item.path_key_index != path_key_index),
+                        false,
                     ),
                     (),
                 );
@@ -454,17 +471,20 @@ impl MultiBuffer {
         while let Some(next_excerpt) = to_insert.next() {
             added_new_excerpt = true;
             let before = new_excerpts.summary().len();
-            new_excerpts.update_last(|excerpt| excerpt.has_trailing_newline = true, ());
+            new_excerpts.update_last(
+                |prev_excerpt| {
+                    dbg!("TRAILING INSERT");
+                    prev_excerpt.has_trailing_newline = true;
+                },
+                (),
+            );
             new_excerpts.push(
                 Excerpt::new(
                     path_key.clone(),
                     path_key_index,
                     &buffer_snapshot,
                     next_excerpt.clone(),
-                    to_insert.peek().is_some()
-                        || cursor
-                            .item()
-                            .is_some_and(|item| item.path_key_index != path_key_index),
+                    false,
                 ),
                 (),
             );
@@ -475,8 +495,26 @@ impl MultiBuffer {
             });
         }
 
+        let suffix_start = cursor.position.1;
         let suffix = cursor.suffix();
         let changed_trailing_excerpt = suffix.is_empty();
+        if !suffix.is_empty() {
+            let before = new_excerpts.summary().len();
+            new_excerpts.update_last(
+                |prev_excerpt| {
+                    if !prev_excerpt.has_trailing_newline {
+                        dbg!("BEFORE SUFFIX");
+                        prev_excerpt.has_trailing_newline = true;
+                        patch.push(dbg!(Edit {
+                            old: suffix_start..suffix_start,
+                            new: before..before + MultiBufferOffset(1),
+                        }));
+                    }
+                },
+                (),
+            );
+        }
+        dbg!(&patch);
         new_excerpts.append(suffix, ());
         drop(cursor);
 
@@ -568,6 +606,7 @@ impl MultiBuffer {
             })
         }
         drop(cursor);
+        dbg!("REMOVING");
         if changed_trailing_excerpt {
             snapshot.trailing_excerpt_update_count += 1;
             new_excerpts.update_last(