WIP - Add excerpt headers as a built-in feature of BlockMap

Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
Max Brunsfeld 2022-02-08 18:15:45 -08:00
parent 8b1fb9a2cc
commit c7e2fae9cb
3 changed files with 105 additions and 13 deletions

View file

@ -2,12 +2,13 @@ use super::wrap_map::{self, WrapEdit, WrapPoint, WrapSnapshot};
use crate::{Anchor, ToPoint as _}; use crate::{Anchor, ToPoint as _};
use collections::{HashMap, HashSet}; use collections::{HashMap, HashSet};
use gpui::{AppContext, ElementBox}; use gpui::{AppContext, ElementBox};
use language::Chunk; use language::{BufferSnapshot, Chunk};
use parking_lot::Mutex; use parking_lot::Mutex;
use std::{ use std::{
cmp::{self, Ordering, Reverse}, cmp::{self, Ordering, Reverse},
fmt::Debug, fmt::Debug,
ops::{Deref, Range}, ops::{Deref, Range},
path::Path,
sync::{ sync::{
atomic::{AtomicUsize, Ordering::SeqCst}, atomic::{AtomicUsize, Ordering::SeqCst},
Arc, Arc,
@ -84,13 +85,44 @@ pub enum BlockDisposition {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
struct Transform { struct Transform {
summary: TransformSummary, summary: TransformSummary,
block: Option<AlignedBlock>, block: Option<TransformBlock>,
} }
#[derive(Clone, Debug)] #[derive(Clone)]
pub struct AlignedBlock { enum TransformBlock {
block: Arc<Block>, Custom {
column: u32, block: Arc<Block>,
column: u32,
},
ExcerptHeader {
buffer: BufferSnapshot,
range: Range<text::Anchor>,
path: Option<Arc<Path>>,
},
}
impl TransformBlock {
fn disposition(&self) -> BlockDisposition {
match self {
TransformBlock::Custom { block, column } => block.disposition,
TransformBlock::ExcerptHeader { .. } => BlockDisposition::Above,
}
}
}
impl Debug for TransformBlock {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Custom { block, column } => f
.debug_struct("Custom")
.field("block", block)
.field("column", column)
.finish(),
Self::ExcerptHeader { buffer, path, .. } => {
f.debug_struct("ExcerptHeader").field("path", path).finish()
}
}
}
} }
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
@ -244,12 +276,14 @@ impl BlockMap {
Ok(ix) | Err(ix) => last_block_ix + ix, Ok(ix) | Err(ix) => last_block_ix + ix,
}; };
let end_anchor;
let end_block_ix = if new_end.0 > wrap_snapshot.max_point().row() { let end_block_ix = if new_end.0 > wrap_snapshot.max_point().row() {
end_anchor = Anchor::max();
self.blocks.len() self.blocks.len()
} else { } else {
let new_buffer_end = let new_buffer_end =
wrap_snapshot.to_point(WrapPoint::new(new_end.0, 0), Bias::Left); wrap_snapshot.to_point(WrapPoint::new(new_end.0, 0), Bias::Left);
let end_anchor = buffer.anchor_before(new_buffer_end); end_anchor = buffer.anchor_before(new_buffer_end);
match self.blocks[start_block_ix..].binary_search_by(|probe| { match self.blocks[start_block_ix..].binary_search_by(|probe| {
probe probe
.position .position
@ -276,25 +310,44 @@ impl BlockMap {
} }
} }
let position = wrap_snapshot.from_point(position, Bias::Left); let position = wrap_snapshot.from_point(position, Bias::Left);
(position.row(), column, block.clone()) (
position.row(),
TransformBlock::Custom {
block: block.clone(),
column,
},
)
}),
);
blocks_in_edit.extend(
buffer
.excerpt_boundaries_in_range(start_anchor..end_anchor)
.map(|excerpt_boundary| {
(
excerpt_boundary.row,
TransformBlock::ExcerptHeader {
buffer: excerpt_boundary.buffer,
range: excerpt_boundary.range,
path: excerpt_boundary.path,
},
)
}), }),
); );
// When multiple blocks are on the same row, newer blocks appear above older // When multiple blocks are on the same row, newer blocks appear above older
// blocks. This is arbitrary, but we currently rely on it in ProjectDiagnosticsEditor. // blocks. This is arbitrary, but we currently rely on it in ProjectDiagnosticsEditor.
blocks_in_edit blocks_in_edit.sort();
.sort_by_key(|(row, _, block)| (*row, block.disposition, Reverse(block.id)));
// For each of these blocks, insert a new isomorphic transform preceding the block, // For each of these blocks, insert a new isomorphic transform preceding the block,
// and then insert the block itself. // and then insert the block itself.
for (block_row, column, block) in blocks_in_edit.drain(..) { for (block_row, block) in blocks_in_edit.drain(..) {
let insertion_row = match block.disposition { let insertion_row = match block.disposition() {
BlockDisposition::Above => block_row, BlockDisposition::Above => block_row,
BlockDisposition::Below => block_row + 1, BlockDisposition::Below => block_row + 1,
}; };
let extent_before_block = insertion_row - new_transforms.summary().input_rows; let extent_before_block = insertion_row - new_transforms.summary().input_rows;
push_isomorphic(&mut new_transforms, extent_before_block); push_isomorphic(&mut new_transforms, extent_before_block);
new_transforms.push(Transform::block(block, column), &()); new_transforms.push(Transform::block(block), &());
} }
old_end = WrapRow(old_end.0.min(old_row_count)); old_end = WrapRow(old_end.0.min(old_row_count));

View file

@ -3,6 +3,7 @@ mod element;
pub mod items; pub mod items;
pub mod movement; pub mod movement;
mod multi_buffer; mod multi_buffer;
mod multi_editor;
#[cfg(test)] #[cfg(test)]
mod test; mod test;

View file

@ -15,6 +15,7 @@ use std::{
cmp, fmt, io, cmp, fmt, io,
iter::{self, FromIterator}, iter::{self, FromIterator},
ops::{Range, Sub}, ops::{Range, Sub},
path::Path,
str, str,
sync::Arc, sync::Arc,
time::{Duration, Instant}, time::{Duration, Instant},
@ -101,6 +102,14 @@ pub struct ExcerptProperties<'a, T> {
pub range: Range<T>, pub range: Range<T>,
} }
pub struct ExcerptBoundary {
pub row: u32,
pub buffer: BufferSnapshot,
pub path: Option<Arc<Path>>,
pub range: Range<text::Anchor>,
pub starts_new_buffer: bool,
}
#[derive(Clone)] #[derive(Clone)]
struct Excerpt { struct Excerpt {
id: ExcerptId, id: ExcerptId,
@ -1769,6 +1778,24 @@ impl MultiBufferSnapshot {
start_id != end_id start_id != end_id
} }
pub fn excerpt_boundaries_in_range<'a, T: ToOffset>(
&'a self,
range: Range<T>,
) -> impl Iterator<Item = ExcerptBoundary> + 'a {
let start = range.start.to_offset(self);
let end = range.end.to_offset(self);
let mut cursor = self.excerpts.cursor::<(usize, Option<&ExcerptId>)>();
cursor.seek(&start, Bias::Right, &());
let prev_buffer_id = cursor.prev_item().map(|excerpt| excerpt.buffer_id);
std::iter::from_fn(move || {
let excerpt = cursor.item()?;
let starts_new_buffer = Some(excerpt.buffer_id) != prev_buffer_id;
todo!()
})
}
pub fn parse_count(&self) -> usize { pub fn parse_count(&self) -> usize {
self.parse_count self.parse_count
} }
@ -2628,6 +2655,17 @@ mod tests {
assert!(!snapshot.range_contains_excerpt_boundary(Point::new(4, 0)..Point::new(4, 2))); assert!(!snapshot.range_contains_excerpt_boundary(Point::new(4, 0)..Point::new(4, 2)));
assert!(!snapshot.range_contains_excerpt_boundary(Point::new(4, 2)..Point::new(4, 2))); assert!(!snapshot.range_contains_excerpt_boundary(Point::new(4, 2)..Point::new(4, 2)));
assert_eq!(
snapshot
.excerpt_boundaries_in_range(Point::new(0, 0)..Point::new(4, 2))
.collect::<Vec<_>>(),
&[
(Some(buffer_1.clone()), true),
(Some(buffer_1.clone()), false),
(Some(buffer_2.clone()), false),
]
);
buffer_1.update(cx, |buffer, cx| { buffer_1.update(cx, |buffer, cx| {
buffer.edit( buffer.edit(
[ [