Checkpoint

Co-Authored-By: Nathan <nathan@zed.dev>
This commit is contained in:
Antonio Scandurra
2024-09-23 18:57:06 -06:00
parent fb0cebd3d1
commit a67789d6d7

View File

@@ -1,15 +1,18 @@
use gpui::{AppContext, Context, Model, ModelContext};
use language::{Buffer, BufferSnapshot, OffsetRangeExt, ReplicaId, ToOffset};
use gpui::{Model, ModelContext};
use language::{Bias, Buffer, BufferSnapshot, OffsetRangeExt as _, ReplicaId};
use std::{
cmp,
cmp::{self, Ordering},
fmt::{self, Debug, Formatter},
ops::Range,
path::{Path, PathBuf},
path::Path,
sync::Arc,
};
use sum_tree::{SumTree, TreeMap};
use sum_tree::{SeekTarget, SumTree, TreeMap};
use text::TextSummary;
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
const NEWLINES: &[u8] = &[b'\n'; u8::MAX as usize];
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
struct BufferId {
remote_id: text::BufferId,
replica_id: ReplicaId,
@@ -26,11 +29,13 @@ impl MultiBuffer {
}
}
pub fn insert_excerpts<T: ToOffset>(
pub fn insert_excerpts<T: language::ToOffset>(
&mut self,
new_excerpts: impl IntoIterator<Item = (Model<Buffer>, Range<T>)>,
cx: &mut ModelContext<Self>,
) {
self.sync(cx);
let mut new_excerpts = new_excerpts
.into_iter()
.map(|(buffer_handle, range)| {
@@ -44,7 +49,7 @@ impl MultiBuffer {
},
range,
};
(buffer, key)
(buffer_handle, key)
})
.collect::<Vec<_>>();
new_excerpts.sort_unstable_by_key(|(_, key)| key.clone());
@@ -57,19 +62,119 @@ impl MultiBuffer {
false
}
});
let mut cursor = self.snapshot.excerpts.cursor::<Option<ExcerptKey>>(&());
let mut new_tree = SumTree::<Excerpt>::default();
for (buffer, key) in new_excerpts {
let start_offset = ExcerptOffset {
path: key.path.clone(),
buffer_id: key.buffer_id,
offset: key.range.start,
};
let end_offset = ExcerptOffset {
path: key.path.clone(),
buffer_id: key.buffer_id,
offset: key.range.end,
};
if start_offset.cmp(cursor.start(), &()).is_ge() {
new_tree.append(cursor.slice(&start_offset, Bias::Left, &()), &());
if let Some(excerpt) = cursor.item() {
if excerpt.key.intersects(&key) {
push_excerpt(&mut new_tree, excerpt.clone());
cursor.next(&());
}
}
}
push_excerpt(
&mut new_tree,
Excerpt {
key: key.clone(),
snapshot: buffer.read(cx).snapshot(),
text_summary: buffer.read(cx).text_summary_for_range(key.range.clone()),
},
);
if end_offset.cmp(cursor.start(), &()).is_ge() {
cursor.seek(&end_offset, Bias::Left, &());
if let Some(excerpt) = cursor.item() {
if excerpt.key.intersects(&key) {
push_excerpt(&mut new_tree, excerpt.clone());
cursor.next(&());
}
}
}
}
new_tree.append(cursor.suffix(&()), &());
drop(cursor);
self.snapshot.excerpts = new_tree;
}
fn sync(&mut self, cx: &mut ModelContext<Self>) {}
fn snapshot(&mut self, cx: &mut ModelContext<Self>) -> MultiBufferSnapshot {
self.sync(cx);
self.snapshot.clone()
}
}
#[derive(Default)]
fn push_excerpt(excerpts: &mut SumTree<Excerpt>, excerpt: Excerpt) {
let mut excerpt = Some(excerpt);
excerpts.update_last(
|last_excerpt| {
if last_excerpt.key.intersects(&excerpt.as_ref().unwrap().key) {
let excerpt = excerpt.take().unwrap();
last_excerpt.key.range.start =
cmp::min(last_excerpt.key.range.start, excerpt.key.range.start);
last_excerpt.key.range.end =
cmp::max(last_excerpt.key.range.end, excerpt.key.range.end);
last_excerpt.text_summary = last_excerpt
.snapshot
.text_summary_for_range(last_excerpt.key.range.clone());
}
},
&(),
);
if let Some(excerpt) = excerpt {
excerpts.push(excerpt, &());
}
}
#[derive(Clone, Default)]
pub struct MultiBufferSnapshot {
excerpts: SumTree<Excerpt>,
snapshots: TreeMap<BufferId, BufferSnapshot>,
}
impl MultiBufferSnapshot {
#[cfg(any(test, feature = "test-support"))]
fn text(&self) -> String {
let mut text = String::new();
for excerpt in self.excerpts.iter() {
text.push('\n');
text.extend(excerpt.snapshot.text_for_range(excerpt.key.range.clone()));
}
text
}
pub fn len(&self) -> usize {
self.excerpts.summary().text.len
}
pub fn chunks<T: ToOffset>(&self, range: Range<T>, language_aware: bool) {
todo!()
}
}
#[derive(Clone)]
struct Excerpt {
key: ExcerptKey,
snapshot: BufferSnapshot,
text_summary: TextSummary,
}
impl Debug for Excerpt {
@@ -86,6 +191,7 @@ impl sum_tree::Item for Excerpt {
fn summary(&self) -> Self::Summary {
ExcerptSummary {
max_key: Some(self.key.clone()),
text: self.text_summary.clone(),
}
}
}
@@ -93,6 +199,7 @@ impl sum_tree::Item for Excerpt {
#[derive(Clone, Debug, Default)]
struct ExcerptSummary {
max_key: Option<ExcerptKey>,
text: TextSummary,
}
#[derive(Clone, Debug, Eq, PartialEq)]
@@ -115,8 +222,8 @@ impl Ord for ExcerptKey {
self.path
.cmp(&other.path)
.then_with(|| self.buffer_id.cmp(&other.buffer_id))
.then_with(|| self.range.start.cmp(&other.range.start))
.then_with(|| other.range.end.cmp(&self.range.end))
.then_with(|| Ord::cmp(&self.range.start, &other.range.start))
.then_with(|| Ord::cmp(&other.range.end, &self.range.end))
}
}
@@ -126,6 +233,26 @@ impl PartialOrd for ExcerptKey {
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
struct ExcerptOffset {
path: Option<Arc<Path>>,
buffer_id: BufferId,
offset: usize,
}
impl<'a> sum_tree::SeekTarget<'a, ExcerptSummary, Option<ExcerptKey>> for ExcerptOffset {
fn cmp(&self, cursor_location: &Option<ExcerptKey>, _: &()) -> Ordering {
if let Some(cursor_location) = cursor_location {
self.path
.cmp(&cursor_location.path)
.then_with(|| self.buffer_id.cmp(&cursor_location.buffer_id))
.then_with(|| Ord::cmp(&self.offset, &cursor_location.range.end))
} else {
Ordering::Greater
}
}
}
impl sum_tree::Summary for ExcerptSummary {
type Context = ();
@@ -133,26 +260,73 @@ impl sum_tree::Summary for ExcerptSummary {
Self::default()
}
fn add_summary(&mut self, summary: &Self, cx: &Self::Context) {
fn add_summary(&mut self, summary: &Self, _cx: &Self::Context) {
self.max_key = summary.max_key.clone();
self.text.add_summary(&summary.text, &());
}
}
impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for usize {
fn zero(_cx: &()) -> Self {
0
}
fn add_summary(&mut self, summary: &'a ExcerptSummary, _cx: &()) {
*self += summary.text.len;
}
}
impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for Option<ExcerptKey> {
fn zero(_cx: &()) -> Self {
None
}
fn add_summary(&mut self, summary: &'a ExcerptSummary, _cx: &()) {
debug_assert!(summary.max_key >= *self);
*self = summary.max_key.clone();
}
}
pub trait ToOffset {
fn to_offset(&self, snapshot: &MultiBufferSnapshot) -> usize;
}
impl ToOffset for usize {
fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize {
assert!(*self <= snapshot.len(), "offset is out of range");
*self
}
}
#[cfg(test)]
mod tests {
use super::*;
use gpui::AppContext;
use gpui::{AppContext, Context};
use language::Buffer;
#[gpui::test]
fn test_insert_excerpts(cx: &mut AppContext) {
let buffer1 = cx.new_model(|cx| Buffer::local("abc\ndef\nghi", cx));
let buffer2 = cx.new_model(|cx| Buffer::local("jkl\nmno\npqr", cx));
let buffer1 = cx.new_model(|cx| Buffer::local("abcdefghijklmnopqrstuvwxyz", cx));
cx.new_model(|cx| {
let mut multibuffer = MultiBuffer::new();
multibuffer
.insert_excerpts(vec![(buffer1.clone(), 0..2), (buffer1.clone(), 4..12)], cx);
assert_eq!(multibuffer.snapshot(cx).text(), "\nab\nefghijkl");
let multi = cx.new_model(|cx| {
let mut multi = MultiBuffer::new();
multi.insert_excerpts(vec![(buffer1.clone(), 0..4), (buffer2.clone(), 8..11)], cx);
multi
multibuffer
.insert_excerpts(vec![(buffer1.clone(), 4..6), (buffer1.clone(), 8..10)], cx);
assert_eq!(multibuffer.snapshot(cx).text(), "\nab\nefghijkl");
multibuffer.insert_excerpts(
vec![(buffer1.clone(), 10..14), (buffer1.clone(), 16..18)],
cx,
);
assert_eq!(multibuffer.snapshot(cx).text(), "\nab\nefghijklmn\nqr");
multibuffer.insert_excerpts(vec![(buffer1.clone(), 12..17)], cx);
assert_eq!(multibuffer.snapshot(cx).text(), "\nab\nefghijklmnopqr");
multibuffer
});
}
}