From a02a29944c11abf5ed6b4b7777362b84e1ab2f5a Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 6 Dec 2021 13:01:09 -0700 Subject: [PATCH] Get the basic ExcerptList unit test passing again Co-Authored-By: Max Brunsfeld --- crates/language/src/excerpt_list.rs | 202 ++++++++++++++-------------- 1 file changed, 100 insertions(+), 102 deletions(-) diff --git a/crates/language/src/excerpt_list.rs b/crates/language/src/excerpt_list.rs index bd47965e10..59619f156e 100644 --- a/crates/language/src/excerpt_list.rs +++ b/crates/language/src/excerpt_list.rs @@ -1,11 +1,12 @@ use crate::{buffer, Buffer, Chunk}; use collections::HashMap; use gpui::{AppContext, Entity, ModelContext, ModelHandle}; +use lsp::TextDocumentSaveReason; use parking_lot::Mutex; use smallvec::{smallvec, SmallVec}; use std::{cmp, iter, mem, ops::Range}; use sum_tree::{Bias, Cursor, SumTree}; -use text::{Anchor, AnchorRangeExt, TextSummary}; +use text::{Anchor, AnchorRangeExt, Patch, TextSummary}; use theme::SyntaxTheme; const NEWLINES: &'static [u8] = &[b'\n'; u8::MAX as usize]; @@ -22,15 +23,16 @@ pub struct ExcerptList { buffers: HashMap, } +#[derive(Debug)] struct BufferState { buffer: ModelHandle, - subscription: text::Subscription, + last_sync: clock::Global, excerpts: Vec, } #[derive(Clone, Default)] pub struct Snapshot { - entries: SumTree, + excerpts: SumTree, } pub struct ExcerptProperties<'a, T> { @@ -40,10 +42,10 @@ pub struct ExcerptProperties<'a, T> { } #[derive(Clone)] -struct Entry { +struct Excerpt { id: ExcerptId, buffer: buffer::Snapshot, - buffer_range: Range, + range: Range, text_summary: TextSummary, header_height: u8, } @@ -55,11 +57,11 @@ struct EntrySummary { } #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub struct Location(SmallVec<[usize; 4]>); +pub struct Location(SmallVec<[u8; 4]>); pub struct Chunks<'a> { range: Range, - cursor: Cursor<'a, Entry, usize>, + cursor: Cursor<'a, Excerpt, usize>, header_height: u8, entry_chunks: Option>, theme: Option<&'a SyntaxTheme>, @@ -82,40 +84,21 @@ impl ExcerptList { self.sync(cx); let buffer = props.buffer.read(cx); - let buffer_range = - buffer.anchor_before(props.range.start)..buffer.anchor_after(props.range.end); - let mut text_summary = - buffer.text_summary_for_range::(buffer_range.clone()); - if props.header_height > 0 { - text_summary.first_line_chars = 0; - text_summary.lines.row += props.header_height as u32; - text_summary.lines_utf16.row += props.header_height as u32; - text_summary.bytes += props.header_height as usize; - } - + let range = buffer.anchor_before(props.range.start)..buffer.anchor_after(props.range.end); let mut snapshot = self.snapshot.lock(); - let prev_id = snapshot.entries.last().map(|e| &e.id); + let prev_id = snapshot.excerpts.last().map(|e| &e.id); let id = ExcerptId::between(prev_id.unwrap_or(&ExcerptId::min()), &ExcerptId::max()); - snapshot.entries.push( - Entry { - id: id.clone(), - buffer: props.buffer.read(cx).snapshot(), - buffer_range, - text_summary, - header_height: props.header_height, - }, + + snapshot.excerpts.push( + Excerpt::new(id.clone(), buffer.snapshot(), range, props.header_height), &(), ); - self.buffers .entry(props.buffer.id()) - .or_insert_with(|| { - let subscription = props.buffer.update(cx, |buffer, _| buffer.subscribe()); - BufferState { - buffer: props.buffer.clone(), - subscription, - excerpts: Default::default(), - } + .or_insert_with(|| BufferState { + buffer: props.buffer.clone(), + last_sync: buffer.version(), + excerpts: Default::default(), }) .excerpts .push(id.clone()); @@ -125,78 +108,66 @@ impl ExcerptList { fn sync(&self, cx: &AppContext) { let mut snapshot = self.snapshot.lock(); - let mut patches = Vec::new(); let mut excerpts_to_edit = Vec::new(); for buffer_state in self.buffers.values() { - let patch = buffer_state.subscription.consume(); - if !patch.is_empty() { - let patch_ix = patches.len(); - patches.push(patch); + if buffer_state + .buffer + .read(cx) + .version() + .gt(&buffer_state.last_sync) + { excerpts_to_edit.extend( buffer_state .excerpts .iter() - .map(|excerpt_id| (&buffer_state.buffer, excerpt_id, patch_ix)), - ) + .map(|excerpt_id| (excerpt_id, buffer_state)), + ); } } - excerpts_to_edit.sort_unstable_by_key(|(_, excerpt_id, _)| *excerpt_id); + excerpts_to_edit.sort_unstable_by_key(|(excerpt_id, _)| *excerpt_id); - let old_excerpts = mem::take(&mut snapshot.entries); - let mut cursor = old_excerpts.cursor::(); - for (buffer, excerpt_id, patch_ix) in excerpts_to_edit { - let buffer = buffer.read(cx); - snapshot - .entries - .push_tree(cursor.slice(excerpt_id, Bias::Left, &()), &()); + dbg!(&excerpts_to_edit); - let excerpt = cursor.item().unwrap(); - let mut new_range = excerpt.buffer_range.to_offset(buffer); - for edit in patches[patch_ix].edits() { - let edit_start = edit.new.start; - let edit_end = edit.new.start + edit.old_len(); - if edit_start > new_range.end { - break; - } else if edit_end < new_range.start { - let delta = edit.new_len() as isize - edit.old_len() as isize; - new_range.start = (new_range.start as isize + delta) as usize; - new_range.end = (new_range.end as isize + delta) as usize; - } else { - let mut new_range_len = new_range.len(); - new_range_len -= - cmp::min(new_range.end, edit_end) - cmp::max(new_range.start, edit_start); - if edit_start < new_range.start { - new_range.start = edit.new.end; - } else { - new_range_len += edit.new_len(); - } + let mut patch = Patch::::default(); + let mut new_excerpts = SumTree::new(); + let mut cursor = snapshot.excerpts.cursor::<(ExcerptId, usize)>(); - new_range.end = new_range.start + new_range_len; - } - } - - let mut text_summary: TextSummary = buffer.text_summary_for_range(new_range.clone()); - if excerpt.header_height > 0 { - text_summary.first_line_chars = 0; - text_summary.lines.row += excerpt.header_height as u32; - text_summary.lines_utf16.row += excerpt.header_height as u32; - text_summary.bytes += excerpt.header_height as usize; - } - snapshot.entries.push( - Entry { - id: excerpt.id.clone(), - buffer: buffer.snapshot(), - buffer_range: buffer.anchor_before(new_range.start) - ..buffer.anchor_after(new_range.end), - text_summary, - header_height: excerpt.header_height, - }, + for (id, buffer_state) in excerpts_to_edit { + 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, + ), &(), ); + let edits = buffer + .edits_since_in_range::( + old_excerpt.buffer.version(), + old_excerpt.range.clone(), + ) + .map(|mut edit| { + let excerpt_old_start = cursor.start().1; + let excerpt_new_start = new_excerpts.summary().text.bytes; + edit.old.start += excerpt_old_start; + edit.old.end += excerpt_old_start; + edit.new.start += excerpt_new_start; + edit.new.end += excerpt_new_start; + edit + }); + patch = patch.compose(edits); + cursor.next(&()); } - snapshot.entries.push_tree(cursor.suffix(&()), &()); + new_excerpts.push_tree(cursor.suffix(&()), &()); + + drop(cursor); + snapshot.excerpts = new_excerpts; } } @@ -212,7 +183,7 @@ impl Snapshot { } pub fn len(&self) -> usize { - self.entries.summary().text.bytes + self.excerpts.summary().text.bytes } pub fn chunks<'a, T: ToOffset>( @@ -221,11 +192,11 @@ impl Snapshot { theme: Option<&'a SyntaxTheme>, ) -> Chunks<'a> { let range = range.start.to_offset(self)..range.end.to_offset(self); - let mut cursor = self.entries.cursor::(); + let mut cursor = self.excerpts.cursor::(); cursor.seek(&range.start, Bias::Right, &()); let entry_chunks = cursor.item().map(|entry| { - let buffer_range = entry.buffer_range.to_offset(&entry.buffer); + 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, @@ -245,7 +216,31 @@ impl Snapshot { } } -impl sum_tree::Item for Entry { +impl Excerpt { + fn new( + id: ExcerptId, + buffer: buffer::Snapshot, + range: Range, + header_height: u8, + ) -> Self { + let mut text_summary = buffer.text_summary_for_range::(range.clone()); + if header_height > 0 { + text_summary.first_line_chars = 0; + text_summary.lines.row += header_height as u32; + text_summary.lines_utf16.row += header_height as u32; + text_summary.bytes += header_height as usize; + } + Excerpt { + id, + buffer, + range, + text_summary, + header_height, + } + } +} + +impl sum_tree::Item for Excerpt { type Summary = EntrySummary; fn summary(&self) -> Self::Summary { @@ -272,7 +267,7 @@ impl<'a> sum_tree::Dimension<'a, EntrySummary> for usize { } } -impl<'a> sum_tree::Dimension<'a, EntrySummary> for ExcerptId { +impl<'a> sum_tree::Dimension<'a, EntrySummary> for Location { fn add_summary(&mut self, summary: &'a EntrySummary, _: &()) { debug_assert!(summary.excerpt_id > *self); *self = summary.excerpt_id.clone(); @@ -304,7 +299,7 @@ impl<'a> Iterator for Chunks<'a> { self.cursor.next(&()); let entry = self.cursor.item()?; - let buffer_range = entry.buffer_range.to_offset(&entry.buffer); + let buffer_range = entry.range.to_offset(&entry.buffer); let buffer_end = cmp::min( buffer_range.end, buffer_range.start + (self.range.end - self.cursor.start()), @@ -338,16 +333,16 @@ impl Default for Location { impl Location { pub fn min() -> Self { - Self(smallvec![usize::MIN]) + Self(smallvec![u8::MIN]) } pub fn max() -> Self { - Self(smallvec![usize::MAX]) + Self(smallvec![u8::MAX]) } pub fn between(lhs: &Self, rhs: &Self) -> Self { - let lhs = lhs.0.iter().copied().chain(iter::repeat(usize::MIN)); - let rhs = rhs.0.iter().copied().chain(iter::repeat(usize::MAX)); + let lhs = lhs.0.iter().copied().chain(iter::repeat(u8::MIN)); + let rhs = rhs.0.iter().copied().chain(iter::repeat(u8::MAX)); let mut location = SmallVec::new(); for (lhs, rhs) in lhs.zip(rhs) { let mid = lhs + (rhs.saturating_sub(lhs)) / 2; @@ -378,7 +373,10 @@ mod tests { let list = cx.add_model(|cx| { let mut list = ExcerptList::new(); - + // aaaaaa + // bbbbbb + // cccccc + // dddddd list.push( ExcerptProperties { buffer: &buffer_1,