diff --git a/crates/language/src/excerpt_list.rs b/crates/language/src/excerpt_list.rs index 66b052598d..1d0caa291e 100644 --- a/crates/language/src/excerpt_list.rs +++ b/crates/language/src/excerpt_list.rs @@ -3,7 +3,10 @@ use collections::HashMap; use gpui::{AppContext, Entity, ModelContext, ModelHandle}; use parking_lot::Mutex; use smallvec::{smallvec, SmallVec}; -use std::{cmp, iter, ops::Range}; +use std::{ + cmp, iter, + ops::{Deref, Range}, +}; use sum_tree::{Bias, Cursor, SumTree}; use text::{ subscription::{Subscription, Topic}, @@ -146,15 +149,6 @@ impl ExcerptList { new_excerpts.push_tree(cursor.slice(id, Bias::Left, &()), &()); let old_excerpt = cursor.item().unwrap(); let buffer = buffer_state.buffer.read(cx); - new_excerpts.push( - Excerpt::new( - id.clone(), - buffer.snapshot(), - old_excerpt.range.clone(), - old_excerpt.header_height, - ), - &(), - ); edits.extend( buffer @@ -163,8 +157,10 @@ impl ExcerptList { old_excerpt.range.clone(), ) .map(|mut edit| { - let excerpt_old_start = cursor.start().1; - let excerpt_new_start = new_excerpts.summary().text.bytes; + let excerpt_old_start = + cursor.start().1 + old_excerpt.header_height as usize; + let excerpt_new_start = + new_excerpts.summary().text.bytes + old_excerpt.header_height as usize; edit.old.start += excerpt_old_start; edit.old.end += excerpt_old_start; edit.new.start += excerpt_new_start; @@ -173,6 +169,16 @@ impl ExcerptList { }), ); + new_excerpts.push( + Excerpt::new( + id.clone(), + buffer.snapshot(), + old_excerpt.range.clone(), + old_excerpt.header_height, + ), + &(), + ); + cursor.next(&()); } new_excerpts.push_tree(cursor.suffix(&()), &()); @@ -195,6 +201,13 @@ impl Snapshot { .collect() } + pub fn text_for_range<'a, T: ToOffset>( + &'a self, + range: Range, + ) -> impl Iterator { + self.chunks(range, None).map(|chunk| chunk.text) + } + pub fn len(&self) -> usize { self.excerpts.summary().text.bytes } @@ -208,16 +221,34 @@ impl Snapshot { let mut cursor = self.excerpts.cursor::(); cursor.seek(&range.start, Bias::Right, &()); - let entry_chunks = cursor.item().map(|entry| { - let buffer_range = entry.range.to_offset(&entry.buffer); - let buffer_start = buffer_range.start + (range.start - cursor.start()); - let buffer_end = cmp::min( - buffer_range.end, - buffer_range.start + (range.end - cursor.start()), - ); - entry.buffer.chunks(buffer_start..buffer_end, theme) + let mut header_height: u8 = 0; + let entry_chunks = cursor.item().map(|excerpt| { + let buffer_range = excerpt.range.to_offset(&excerpt.buffer); + header_height = excerpt.header_height; + let start_overshoot = range.start - cursor.start(); + let buffer_start; + if start_overshoot < excerpt.header_height as usize { + header_height -= start_overshoot as u8; + buffer_start = buffer_range.start; + } else { + buffer_start = buffer_range.start + start_overshoot - header_height as usize; + header_height = 0; + } + + let end_overshoot = range.end - cursor.start(); + let buffer_end; + if end_overshoot < excerpt.header_height as usize { + header_height -= excerpt.header_height - end_overshoot as u8; + buffer_end = buffer_start; + } else { + buffer_end = cmp::min( + buffer_range.end, + buffer_range.start + end_overshoot - header_height as usize, + ); + } + + excerpt.buffer.chunks(buffer_start..buffer_end, theme) }); - let header_height = cursor.item().map_or(0, |entry| entry.header_height); Chunks { range, @@ -242,7 +273,15 @@ impl Excerpt { text_summary.lines.row += header_height as u32; text_summary.lines_utf16.row += header_height as u32; text_summary.bytes += header_height as usize; + text_summary.longest_row += header_height as u32; } + text_summary.last_line_chars = 0; + text_summary.lines.row += 1; + text_summary.lines.column = 0; + text_summary.lines_utf16.row += 1; + text_summary.lines_utf16.column = 0; + text_summary.bytes += 1; + Excerpt { id, buffer, @@ -307,6 +346,10 @@ impl<'a> Iterator for Chunks<'a> { return Some(chunk); } else { self.entry_chunks.take(); + return Some(Chunk { + text: "\n", + ..Default::default() + }); } } @@ -325,10 +368,7 @@ impl<'a> Iterator for Chunks<'a> { .chunks(buffer_range.start..buffer_end, self.theme), ); - Some(Chunk { - text: "\n", - ..Default::default() - }) + self.next() } } @@ -399,7 +439,7 @@ mod tests { subscription.consume().into_inner(), [Edit { old: 0..0, - new: 0..12 + new: 0..13 }] ); @@ -422,8 +462,8 @@ mod tests { assert_eq!( subscription.consume().into_inner(), [Edit { - old: 12..12, - new: 12..26 + old: 13..13, + new: 13..29 }] ); @@ -443,7 +483,7 @@ mod tests { "\n", // "\n", // "\n", // - "jj" // + "jj\n" // ) ); @@ -472,15 +512,15 @@ mod tests { "\n", // "\n", // "\n", // - "jj" // + "jj\n" // ) ); assert_eq!( subscription.consume().into_inner(), [Edit { - old: 17..19, - new: 17..18 + old: 18..20, + new: 18..19 }] ); } @@ -495,12 +535,13 @@ mod tests { let list = cx.add_model(|_| ExcerptList::new()); let mut excerpt_ids = Vec::new(); let mut expected_excerpts = Vec::new(); + let mut old_versions = Vec::new(); for _ in 0..operations { match rng.gen_range(0..100) { 0..=19 if !buffers.is_empty() => { let buffer = buffers.choose(&mut rng).unwrap(); - buffer.update(cx, |buf, cx| buf.randomly_edit(&mut rng, 5, cx)); + buffer.update(cx, |buf, cx| buf.randomly_edit(&mut rng, 1, cx)); } _ => { let buffer_handle = if buffers.is_empty() || rng.gen_bool(0.4) { @@ -539,21 +580,53 @@ mod tests { } } + if rng.gen_bool(0.3) { + list.update(cx, |list, cx| { + old_versions.push((list.snapshot(cx), list.subscribe())); + }) + } + let snapshot = list.read(cx).snapshot(cx); let mut expected_text = String::new(); for (buffer, range, header_height) in &expected_excerpts { let buffer = buffer.read(cx); - if !expected_text.is_empty() { - expected_text.push('\n'); - } - for _ in 0..*header_height { expected_text.push('\n'); } expected_text.extend(buffer.text_for_range(range.clone())); + expected_text.push('\n'); } assert_eq!(snapshot.text(), expected_text); + + for i in 0..10 { + let end_ix = snapshot.clip_offset(rng.gen_range(0..=snapshot.len()), Bias::Right); + let start_ix = snapshot.clip_offset(rng.gen_range(0..=end_ix), Bias::Left); + + let actual = snapshot.text_for_range(start_ix..end_ix).collect(); + } + // for i in 0..expected_text.len() { + // assert_eq!(snapshot.text(), expected_text); + + // } } + + // let snapshot = list.read(cx).snapshot(cx); + // for (old_snapshot, subscription) in old_versions { + // let edits = subscription.consume().into_inner(); + + // log::info!( + // "applying edits since old text: {:?}: {:?}", + // old_snapshot.text(), + // edits, + // ); + + // let mut text = old_snapshot.text(); + // for edit in edits { + // let new_text: String = snapshot.text_for_range(edit.new.clone()).collect(); + // text.replace_range(edit.new.start..edit.new.start + edit.old.len(), &new_text); + // } + // assert_eq!(text.to_string(), snapshot.text()); + // } } #[gpui::test(iterations = 100)]