diff --git a/crates/editor/src/multi_buffer.rs b/crates/editor/src/multi_buffer.rs index 04860c5937..3176bb3ea3 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -4,7 +4,7 @@ pub use anchor::{Anchor, AnchorRangeExt}; use anyhow::Result; use clock::ReplicaId; use collections::{HashMap, HashSet}; -use gpui::{AppContext, ElementBox, Entity, ModelContext, ModelHandle, MutableAppContext, Task}; +use gpui::{AppContext, ElementBox, Entity, ModelContext, ModelHandle, Task}; use language::{ Buffer, BufferChunks, BufferSnapshot, Chunk, DiagnosticEntry, Event, File, Language, Selection, ToOffset as _, ToPoint as _, TransactionId, @@ -171,7 +171,7 @@ impl MultiBuffer { } #[cfg(any(test, feature = "test-support"))] - pub fn build_simple(text: &str, cx: &mut MutableAppContext) -> ModelHandle { + pub fn build_simple(text: &str, cx: &mut gpui::MutableAppContext) -> ModelHandle { let buffer = cx.add_model(|cx| Buffer::new(0, text, cx)); cx.add_model(|cx| Self::singleton(buffer, cx)) } @@ -180,7 +180,7 @@ impl MultiBuffer { pub fn build_random( excerpts: usize, mut rng: &mut impl rand::Rng, - cx: &mut MutableAppContext, + cx: &mut gpui::MutableAppContext, ) -> ModelHandle { use rand::prelude::*; use text::RandomCharIter; @@ -604,7 +604,6 @@ impl MultiBuffer { snapshot.excerpts.update_last( |excerpt| { excerpt.has_trailing_newline = true; - excerpt.text_summary += TextSummary::from("\n"); prev_id = Some(excerpt.id.clone()); }, &(), @@ -852,10 +851,7 @@ impl MultiBufferSnapshot { cursor.seek(&offset, Bias::Left, &()); let mut excerpt_chunks = cursor.item().map(|excerpt| { let start_after_header = cursor.start() + excerpt.header_height as usize; - let mut end_before_footer = cursor.start() + excerpt.text_summary.bytes; - if excerpt.has_trailing_newline { - end_before_footer -= 1; - } + let end_before_footer = cursor.start() + excerpt.text_summary.bytes; let start = excerpt.range.start.to_offset(&excerpt.buffer); let end = @@ -942,6 +938,12 @@ impl MultiBufferSnapshot { if let Some(excerpt) = cursor.item() { let header_end = *cursor.start() + excerpt.header_height as usize; if offset < header_end { + if bias == Bias::Left { + cursor.prev(&()); + if let Some(excerpt) = cursor.item() { + return *cursor.start() + excerpt.text_summary.bytes; + } + } header_end } else { let excerpt_start = excerpt.range.start.to_offset(&excerpt.buffer); @@ -966,6 +968,12 @@ impl MultiBufferSnapshot { if let Some(excerpt) = cursor.item() { let header_end = *cursor.start() + Point::new(excerpt.header_height as u32, 0); if point < header_end { + if bias == Bias::Left { + cursor.prev(&()); + if let Some(excerpt) = cursor.item() { + return *cursor.start() + excerpt.text_summary.lines; + } + } header_end } else { let excerpt_start = excerpt.range.start.to_point(&excerpt.buffer); @@ -990,6 +998,12 @@ impl MultiBufferSnapshot { if let Some(excerpt) = cursor.item() { let header_end = *cursor.start() + PointUtf16::new(excerpt.header_height as u32, 0); if point < header_end { + if bias == Bias::Left { + cursor.prev(&()); + if let Some(excerpt) = cursor.item() { + return *cursor.start() + excerpt.text_summary.lines_utf16; + } + } header_end } else { let excerpt_start = excerpt @@ -1017,10 +1031,8 @@ impl MultiBufferSnapshot { let mut chunk = &[][..]; let excerpt_bytes = if let Some(excerpt) = excerpts.item() { - let mut excerpt_bytes = excerpt.bytes_in_range( - range.start - excerpts.start() - ..cmp::min(range.end - excerpts.start(), excerpt.text_summary.bytes), - ); + let mut excerpt_bytes = excerpt + .bytes_in_range(range.start - excerpts.start()..range.end - excerpts.start()); chunk = excerpt_bytes.next().unwrap_or(&[][..]); Some(excerpt_bytes) } else { @@ -1611,15 +1623,6 @@ impl Excerpt { text_summary.bytes += header_height as usize; text_summary.longest_row += header_height as u32; } - if has_trailing_newline { - 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_id, @@ -1651,7 +1654,7 @@ impl Excerpt { ) -> ExcerptChunks<'a> { let content_start = self.range.start.to_offset(&self.buffer); let chunks_start = content_start + range.start.saturating_sub(self.header_height as usize); - let mut chunks_end = content_start + let chunks_end = content_start + cmp::min(range.end, self.text_summary.bytes) .saturating_sub(self.header_height as usize); @@ -1659,13 +1662,15 @@ impl Excerpt { (self.header_height as usize).saturating_sub(range.start), range.len(), ); - let mut footer_height = 0; - if self.has_trailing_newline && range.end == self.text_summary.bytes { - chunks_end -= 1; - if !range.is_empty() { - footer_height = 1; - } - } + + let footer_height = if self.has_trailing_newline + && range.start <= self.text_summary.bytes + && range.end > self.text_summary.bytes + { + 1 + } else { + 0 + }; let content_chunks = self.buffer.chunks(chunks_start..chunks_end, theme); @@ -1679,7 +1684,7 @@ impl Excerpt { fn bytes_in_range(&self, range: Range) -> ExcerptBytes { let content_start = self.range.start.to_offset(&self.buffer); let bytes_start = content_start + range.start.saturating_sub(self.header_height as usize); - let mut bytes_end = content_start + let bytes_end = content_start + cmp::min(range.end, self.text_summary.bytes) .saturating_sub(self.header_height as usize); @@ -1687,13 +1692,15 @@ impl Excerpt { (self.header_height as usize).saturating_sub(range.start), range.len(), ); - let mut footer_height = 0; - if self.has_trailing_newline && range.end == self.text_summary.bytes { - bytes_end -= 1; - if !range.is_empty() { - footer_height = 1; - } - } + + let footer_height = if self.has_trailing_newline + && range.start <= self.text_summary.bytes + && range.end > self.text_summary.bytes + { + 1 + } else { + 0 + }; let content_bytes = self.buffer.bytes_in_range(bytes_start..bytes_end); @@ -1740,9 +1747,13 @@ impl sum_tree::Item for Excerpt { type Summary = ExcerptSummary; fn summary(&self) -> Self::Summary { + let mut text = self.text_summary.clone(); + if self.has_trailing_newline { + text += TextSummary::from("\n"); + } ExcerptSummary { excerpt_id: self.id.clone(), - text: self.text_summary.clone(), + text, } } } @@ -1809,11 +1820,7 @@ impl<'a> MultiBufferChunks<'a> { self.excerpts.seek(&offset, Bias::Right, &()); if let Some(excerpt) = self.excerpts.item() { self.excerpt_chunks = Some(excerpt.chunks_in_range( - self.range.start - self.excerpts.start() - ..cmp::min( - self.range.end - self.excerpts.start(), - excerpt.text_summary.bytes, - ), + self.range.start - self.excerpts.start()..self.range.end - self.excerpts.start(), self.theme, )); } else { @@ -1834,13 +1841,9 @@ impl<'a> Iterator for MultiBufferChunks<'a> { } else { self.excerpts.next(&()); let excerpt = self.excerpts.item()?; - self.excerpt_chunks = Some(excerpt.chunks_in_range( - 0..cmp::min( - self.range.end - self.excerpts.start(), - excerpt.text_summary.bytes, - ), - self.theme, - )); + self.excerpt_chunks = Some( + excerpt.chunks_in_range(0..self.range.end - self.excerpts.start(), self.theme), + ); self.next() } } @@ -1857,12 +1860,8 @@ impl<'a> MultiBufferBytes<'a> { } else { self.excerpts.next(&()); if let Some(excerpt) = self.excerpts.item() { - let mut excerpt_bytes = excerpt.bytes_in_range( - 0..cmp::min( - self.range.end - self.excerpts.start(), - excerpt.text_summary.bytes, - ), - ); + let mut excerpt_bytes = + excerpt.bytes_in_range(0..self.range.end - self.excerpts.start()); self.chunk = excerpt_bytes.next().unwrap(); self.excerpt_bytes = Some(excerpt_bytes); } @@ -2149,6 +2148,40 @@ mod tests { new: 8..9 }] ); + + let multibuffer = multibuffer.read(cx).snapshot(cx); + assert_eq!( + multibuffer.clip_point(Point::new(0, 0), Bias::Left), + Point::new(2, 0) + ); + assert_eq!( + multibuffer.clip_point(Point::new(0, 0), Bias::Right), + Point::new(2, 0) + ); + assert_eq!( + multibuffer.clip_point(Point::new(1, 0), Bias::Left), + Point::new(2, 0) + ); + assert_eq!( + multibuffer.clip_point(Point::new(1, 0), Bias::Right), + Point::new(2, 0) + ); + assert_eq!( + multibuffer.clip_point(Point::new(8, 0), Bias::Left), + Point::new(7, 4) + ); + assert_eq!( + multibuffer.clip_point(Point::new(8, 0), Bias::Right), + Point::new(11, 0) + ); + assert_eq!( + multibuffer.clip_point(Point::new(9, 0), Bias::Left), + Point::new(7, 4) + ); + assert_eq!( + multibuffer.clip_point(Point::new(9, 0), Bias::Right), + Point::new(11, 0) + ); } #[gpui::test]