Allow multibuffer to clip to the ends of excerpts, before trailing newlines

This commit is contained in:
Max Brunsfeld 2021-12-15 17:04:17 -08:00
parent f8ef605cbd
commit e8570b5c26

View file

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