mirror of
https://github.com/zed-industries/zed.git
synced 2025-01-12 05:15:00 +00:00
WIP - Add excerpt headers as a built-in feature of BlockMap
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
parent
8b1fb9a2cc
commit
c7e2fae9cb
3 changed files with 105 additions and 13 deletions
|
@ -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));
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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(
|
||||||
[
|
[
|
||||||
|
|
Loading…
Reference in a new issue