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 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<Self> {
pub fn build_simple(text: &str, cx: &mut gpui::MutableAppContext) -> ModelHandle<Self> {
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<Self> {
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<usize>) -> 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]