WIP: Make editor crate compile again

Tests are still failing though.
This commit is contained in:
Antonio Scandurra 2022-02-09 11:09:11 +01:00
parent c7e2fae9cb
commit 0e1318dfe4
7 changed files with 297 additions and 224 deletions

View file

@ -15,8 +15,8 @@ use tab_map::TabMap;
use wrap_map::WrapMap; use wrap_map::WrapMap;
pub use block_map::{ pub use block_map::{
AlignedBlock, BlockBufferRows as DisplayBufferRows, BlockChunks as DisplayChunks, BlockContext, BlockBufferRows as DisplayBufferRows, BlockChunks as DisplayChunks, BlockContext,
BlockDisposition, BlockId, BlockProperties, RenderBlock, BlockDisposition, BlockId, BlockProperties, RenderBlock, TransformBlock,
}; };
pub trait ToDisplayPoint { pub trait ToDisplayPoint {
@ -43,13 +43,14 @@ impl DisplayMap {
font_id: FontId, font_id: FontId,
font_size: f32, font_size: f32,
wrap_width: Option<f32>, wrap_width: Option<f32>,
excerpt_header_height: u8,
cx: &mut ModelContext<Self>, cx: &mut ModelContext<Self>,
) -> Self { ) -> Self {
let buffer_subscription = buffer.update(cx, |buffer, _| buffer.subscribe()); let buffer_subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
let (fold_map, snapshot) = FoldMap::new(buffer.read(cx).snapshot(cx)); let (fold_map, snapshot) = FoldMap::new(buffer.read(cx).snapshot(cx));
let (tab_map, snapshot) = TabMap::new(snapshot, tab_size); let (tab_map, snapshot) = TabMap::new(snapshot, tab_size);
let (wrap_map, snapshot) = WrapMap::new(snapshot, font_id, font_size, wrap_width, cx); let (wrap_map, snapshot) = WrapMap::new(snapshot, font_id, font_size, wrap_width, cx);
let block_map = BlockMap::new(snapshot); let block_map = BlockMap::new(snapshot, excerpt_header_height);
cx.observe(&wrap_map, |_, _, cx| cx.notify()).detach(); cx.observe(&wrap_map, |_, _, cx| cx.notify()).detach();
DisplayMap { DisplayMap {
buffer, buffer,
@ -318,7 +319,7 @@ impl DisplaySnapshot {
pub fn blocks_in_range<'a>( pub fn blocks_in_range<'a>(
&'a self, &'a self,
rows: Range<u32>, rows: Range<u32>,
) -> impl Iterator<Item = (u32, &'a AlignedBlock)> { ) -> impl Iterator<Item = (u32, &'a TransformBlock)> {
self.blocks_snapshot.blocks_in_range(rows) self.blocks_snapshot.blocks_in_range(rows)
} }
@ -471,6 +472,7 @@ mod tests {
let font_cache = cx.font_cache().clone(); let font_cache = cx.font_cache().clone();
let tab_size = rng.gen_range(1..=4); let tab_size = rng.gen_range(1..=4);
let excerpt_header_height = rng.gen_range(1..=5);
let family_id = font_cache.load_family(&["Helvetica"]).unwrap(); let family_id = font_cache.load_family(&["Helvetica"]).unwrap();
let font_id = font_cache let font_id = font_cache
.select_font(family_id, &Default::default()) .select_font(family_id, &Default::default())
@ -497,7 +499,15 @@ mod tests {
}); });
let map = cx.add_model(|cx| { let map = cx.add_model(|cx| {
DisplayMap::new(buffer.clone(), tab_size, font_id, font_size, wrap_width, cx) DisplayMap::new(
buffer.clone(),
tab_size,
font_id,
font_size,
wrap_width,
excerpt_header_height,
cx,
)
}); });
let mut notifications = observe(&map, &mut cx); let mut notifications = observe(&map, &mut cx);
let mut fold_count = 0; let mut fold_count = 0;
@ -711,7 +721,15 @@ mod tests {
let text = "one two three four five\nsix seven eight"; let text = "one two three four five\nsix seven eight";
let buffer = MultiBuffer::build_simple(text, cx); let buffer = MultiBuffer::build_simple(text, cx);
let map = cx.add_model(|cx| { let map = cx.add_model(|cx| {
DisplayMap::new(buffer.clone(), tab_size, font_id, font_size, wrap_width, cx) DisplayMap::new(
buffer.clone(),
tab_size,
font_id,
font_size,
wrap_width,
1,
cx,
)
}); });
let snapshot = map.update(cx, |map, cx| map.snapshot(cx)); let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
@ -791,7 +809,7 @@ mod tests {
.unwrap(); .unwrap();
let font_size = 14.0; let font_size = 14.0;
let map = cx.add_model(|cx| { let map = cx.add_model(|cx| {
DisplayMap::new(buffer.clone(), tab_size, font_id, font_size, None, cx) DisplayMap::new(buffer.clone(), tab_size, font_id, font_size, None, 1, cx)
}); });
buffer.update(cx, |buffer, cx| { buffer.update(cx, |buffer, cx| {
buffer.edit( buffer.edit(
@ -871,7 +889,7 @@ mod tests {
let font_size = 14.0; let font_size = 14.0;
let map = let map =
cx.add_model(|cx| DisplayMap::new(buffer, tab_size, font_id, font_size, None, cx)); cx.add_model(|cx| DisplayMap::new(buffer, tab_size, font_id, font_size, None, 1, cx));
assert_eq!( assert_eq!(
cx.update(|cx| chunks(0..5, &map, &theme, cx)), cx.update(|cx| chunks(0..5, &map, &theme, cx)),
vec![ vec![
@ -958,8 +976,9 @@ mod tests {
.unwrap(); .unwrap();
let font_size = 16.0; let font_size = 16.0;
let map = cx let map = cx.add_model(|cx| {
.add_model(|cx| DisplayMap::new(buffer, tab_size, font_id, font_size, Some(40.0), cx)); DisplayMap::new(buffer, tab_size, font_id, font_size, Some(40.0), 1, cx)
});
assert_eq!( assert_eq!(
cx.update(|cx| chunks(0..5, &map, &theme, cx)), cx.update(|cx| chunks(0..5, &map, &theme, cx)),
[ [
@ -1003,7 +1022,7 @@ mod tests {
.unwrap(); .unwrap();
let font_size = 14.0; let font_size = 14.0;
let map = cx.add_model(|cx| { let map = cx.add_model(|cx| {
DisplayMap::new(buffer.clone(), tab_size, font_id, font_size, None, cx) DisplayMap::new(buffer.clone(), tab_size, font_id, font_size, None, 1, cx)
}); });
let map = map.update(cx, |map, cx| map.snapshot(cx)); let map = map.update(cx, |map, cx| map.snapshot(cx));
@ -1047,7 +1066,7 @@ mod tests {
let font_size = 14.0; let font_size = 14.0;
let map = cx.add_model(|cx| { let map = cx.add_model(|cx| {
DisplayMap::new(buffer.clone(), tab_size, font_id, font_size, None, cx) DisplayMap::new(buffer.clone(), tab_size, font_id, font_size, None, 1, cx)
}); });
let map = map.update(cx, |map, cx| map.snapshot(cx)); let map = map.update(cx, |map, cx| map.snapshot(cx));
assert_eq!(map.text(), "α\nβ \n🏀β γ"); assert_eq!(map.text(), "α\nβ \n🏀β γ");
@ -1105,7 +1124,7 @@ mod tests {
.unwrap(); .unwrap();
let font_size = 14.0; let font_size = 14.0;
let map = cx.add_model(|cx| { let map = cx.add_model(|cx| {
DisplayMap::new(buffer.clone(), tab_size, font_id, font_size, None, cx) DisplayMap::new(buffer.clone(), tab_size, font_id, font_size, None, 1, cx)
}); });
assert_eq!( assert_eq!(
map.update(cx, |map, cx| map.snapshot(cx)).max_point(), map.update(cx, |map, cx| map.snapshot(cx)).max_point(),

View file

@ -5,10 +5,9 @@ use gpui::{AppContext, ElementBox};
use language::{BufferSnapshot, Chunk}; use language::{BufferSnapshot, Chunk};
use parking_lot::Mutex; use parking_lot::Mutex;
use std::{ use std::{
cmp::{self, Ordering, Reverse}, cmp::{self, Ordering},
fmt::Debug, fmt::Debug,
ops::{Deref, Range}, ops::{Deref, Range},
path::Path,
sync::{ sync::{
atomic::{AtomicUsize, Ordering::SeqCst}, atomic::{AtomicUsize, Ordering::SeqCst},
Arc, Arc,
@ -24,6 +23,7 @@ pub struct BlockMap {
wrap_snapshot: Mutex<WrapSnapshot>, wrap_snapshot: Mutex<WrapSnapshot>,
blocks: Vec<Arc<Block>>, blocks: Vec<Arc<Block>>,
transforms: Mutex<SumTree<Transform>>, transforms: Mutex<SumTree<Transform>>,
excerpt_header_height: u8,
} }
pub struct BlockMapWriter<'a>(&'a mut BlockMap); pub struct BlockMapWriter<'a>(&'a mut BlockMap);
@ -89,7 +89,7 @@ struct Transform {
} }
#[derive(Clone)] #[derive(Clone)]
enum TransformBlock { pub enum TransformBlock {
Custom { Custom {
block: Arc<Block>, block: Arc<Block>,
column: u32, column: u32,
@ -97,17 +97,24 @@ enum TransformBlock {
ExcerptHeader { ExcerptHeader {
buffer: BufferSnapshot, buffer: BufferSnapshot,
range: Range<text::Anchor>, range: Range<text::Anchor>,
path: Option<Arc<Path>>, height: u8,
}, },
} }
impl TransformBlock { impl TransformBlock {
fn disposition(&self) -> BlockDisposition { fn disposition(&self) -> BlockDisposition {
match self { match self {
TransformBlock::Custom { block, column } => block.disposition, TransformBlock::Custom { block, .. } => block.disposition,
TransformBlock::ExcerptHeader { .. } => BlockDisposition::Above, TransformBlock::ExcerptHeader { .. } => BlockDisposition::Above,
} }
} }
fn height(&self) -> u8 {
match self {
TransformBlock::Custom { block, .. } => block.height,
TransformBlock::ExcerptHeader { height, .. } => *height,
}
}
} }
impl Debug for TransformBlock { impl Debug for TransformBlock {
@ -118,9 +125,10 @@ impl Debug for TransformBlock {
.field("block", block) .field("block", block)
.field("column", column) .field("column", column)
.finish(), .finish(),
Self::ExcerptHeader { buffer, path, .. } => { Self::ExcerptHeader { buffer, .. } => f
f.debug_struct("ExcerptHeader").field("path", path).finish() .debug_struct("ExcerptHeader")
} .field("path", &buffer.path())
.finish(),
} }
} }
} }
@ -147,7 +155,7 @@ pub struct BlockBufferRows<'a> {
} }
impl BlockMap { impl BlockMap {
pub fn new(wrap_snapshot: WrapSnapshot) -> Self { pub fn new(wrap_snapshot: WrapSnapshot, excerpt_header_height: u8) -> Self {
Self { Self {
next_block_id: AtomicUsize::new(0), next_block_id: AtomicUsize::new(0),
blocks: Vec::new(), blocks: Vec::new(),
@ -156,6 +164,7 @@ impl BlockMap {
&(), &(),
)), )),
wrap_snapshot: Mutex::new(wrap_snapshot), wrap_snapshot: Mutex::new(wrap_snapshot),
excerpt_header_height,
} }
} }
@ -202,7 +211,7 @@ impl BlockMap {
if transform if transform
.block .block
.as_ref() .as_ref()
.map_or(false, |b| b.disposition.is_below()) .map_or(false, |b| b.disposition().is_below())
{ {
new_transforms.push(transform.clone(), &()); new_transforms.push(transform.clone(), &());
cursor.next(&()); cursor.next(&());
@ -227,7 +236,7 @@ impl BlockMap {
if transform if transform
.block .block
.as_ref() .as_ref()
.map_or(false, |b| b.disposition.is_below()) .map_or(false, |b| b.disposition().is_below())
{ {
cursor.next(&()); cursor.next(&());
} else { } else {
@ -248,7 +257,7 @@ impl BlockMap {
if transform if transform
.block .block
.as_ref() .as_ref()
.map_or(false, |b| b.disposition.is_below()) .map_or(false, |b| b.disposition().is_below())
{ {
cursor.next(&()); cursor.next(&());
} else { } else {
@ -328,7 +337,7 @@ impl BlockMap {
TransformBlock::ExcerptHeader { TransformBlock::ExcerptHeader {
buffer: excerpt_boundary.buffer, buffer: excerpt_boundary.buffer,
range: excerpt_boundary.range, range: excerpt_boundary.range,
path: excerpt_boundary.path, height: self.excerpt_header_height,
}, },
) )
}), }),
@ -336,7 +345,23 @@ impl BlockMap {
// 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.sort(); blocks_in_edit.sort_unstable_by(|(row_a, block_a), (row_b, block_b)| {
row_a.cmp(&row_b).then_with(|| match (block_a, block_b) {
(
TransformBlock::ExcerptHeader { .. },
TransformBlock::ExcerptHeader { .. },
) => Ordering::Equal,
(TransformBlock::ExcerptHeader { .. }, _) => Ordering::Less,
(_, TransformBlock::ExcerptHeader { .. }) => Ordering::Greater,
(
TransformBlock::Custom { block: block_a, .. },
TransformBlock::Custom { block: block_b, .. },
) => block_a
.disposition
.cmp(&block_b.disposition)
.then_with(|| block_a.id.cmp(&block_b.id).reverse()),
})
});
// 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.
@ -577,7 +602,7 @@ impl BlockSnapshot {
pub fn blocks_in_range<'a>( pub fn blocks_in_range<'a>(
&'a self, &'a self,
rows: Range<u32>, rows: Range<u32>,
) -> impl Iterator<Item = (u32, &'a AlignedBlock)> { ) -> impl Iterator<Item = (u32, &'a TransformBlock)> {
let mut cursor = self.transforms.cursor::<BlockRow>(); let mut cursor = self.transforms.cursor::<BlockRow>();
cursor.seek(&BlockRow(rows.start), Bias::Right, &()); cursor.seek(&BlockRow(rows.start), Bias::Right, &());
std::iter::from_fn(move || { std::iter::from_fn(move || {
@ -697,7 +722,7 @@ impl BlockSnapshot {
let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(); let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>();
cursor.seek(&BlockRow(block_point.row), Bias::Right, &()); cursor.seek(&BlockRow(block_point.row), Bias::Right, &());
if let Some(transform) = cursor.item() { if let Some(transform) = cursor.item() {
match transform.block.as_ref().map(|b| b.disposition) { match transform.block.as_ref().map(|b| b.disposition()) {
Some(BlockDisposition::Above) => WrapPoint::new(cursor.start().1 .0, 0), Some(BlockDisposition::Above) => WrapPoint::new(cursor.start().1 .0, 0),
Some(BlockDisposition::Below) => { Some(BlockDisposition::Below) => {
let wrap_row = cursor.start().1 .0 - 1; let wrap_row = cursor.start().1 .0 - 1;
@ -726,13 +751,13 @@ impl Transform {
} }
} }
fn block(block: Arc<Block>, column: u32) -> Self { fn block(block: TransformBlock) -> Self {
Self { Self {
summary: TransformSummary { summary: TransformSummary {
input_rows: 0, input_rows: 0,
output_rows: block.height as u32, output_rows: block.height() as u32,
}, },
block: Some(AlignedBlock { block, column }), block: Some(block),
} }
} }
@ -862,32 +887,6 @@ impl BlockDisposition {
} }
} }
impl AlignedBlock {
pub fn height(&self) -> u32 {
self.height as u32
}
pub fn column(&self) -> u32 {
self.column
}
pub fn render(&self, cx: &BlockContext) -> ElementBox {
self.render.lock()(cx)
}
pub fn position(&self) -> &Anchor {
&self.block.position
}
}
impl Deref for AlignedBlock {
type Target = Block;
fn deref(&self) -> &Self::Target {
self.block.as_ref()
}
}
impl<'a> Deref for BlockContext<'a> { impl<'a> Deref for BlockContext<'a> {
type Target = AppContext; type Target = AppContext;
@ -896,6 +895,12 @@ impl<'a> Deref for BlockContext<'a> {
} }
} }
impl Block {
pub fn render(&self, cx: &BlockContext) -> ElementBox {
self.render.lock()(cx)
}
}
impl Debug for Block { impl Debug for Block {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Block") f.debug_struct("Block")
@ -931,6 +936,7 @@ mod tests {
use crate::multi_buffer::MultiBuffer; use crate::multi_buffer::MultiBuffer;
use gpui::{elements::Empty, Element}; use gpui::{elements::Empty, Element};
use rand::prelude::*; use rand::prelude::*;
use std::cmp::Reverse;
use std::env; use std::env;
use text::RandomCharIter; use text::RandomCharIter;
@ -964,7 +970,7 @@ mod tests {
let (fold_map, folds_snapshot) = FoldMap::new(buffer_snapshot.clone()); let (fold_map, folds_snapshot) = FoldMap::new(buffer_snapshot.clone());
let (tab_map, tabs_snapshot) = TabMap::new(folds_snapshot.clone(), 1); let (tab_map, tabs_snapshot) = TabMap::new(folds_snapshot.clone(), 1);
let (wrap_map, wraps_snapshot) = WrapMap::new(tabs_snapshot, font_id, 14.0, None, cx); let (wrap_map, wraps_snapshot) = WrapMap::new(tabs_snapshot, font_id, 14.0, None, cx);
let mut block_map = BlockMap::new(wraps_snapshot.clone()); let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1);
let mut writer = block_map.write(wraps_snapshot.clone(), vec![]); let mut writer = block_map.write(wraps_snapshot.clone(), vec![]);
writer.insert(vec![ writer.insert(vec![
@ -994,9 +1000,10 @@ mod tests {
let blocks = snapshot let blocks = snapshot
.blocks_in_range(0..8) .blocks_in_range(0..8)
.map(|(start_row, block)| { .map(|(start_row, block)| {
let (block, column) = block.as_custom().unwrap();
( (
start_row..start_row + block.height(), start_row..start_row + block.height as u32,
block.column(), column,
block block
.render(&BlockContext { .render(&BlockContext {
cx, cx,
@ -1142,7 +1149,7 @@ mod tests {
let (_, folds_snapshot) = FoldMap::new(buffer_snapshot.clone()); let (_, folds_snapshot) = FoldMap::new(buffer_snapshot.clone());
let (_, tabs_snapshot) = TabMap::new(folds_snapshot.clone(), 1); let (_, tabs_snapshot) = TabMap::new(folds_snapshot.clone(), 1);
let (_, wraps_snapshot) = WrapMap::new(tabs_snapshot, font_id, 14.0, Some(60.), cx); let (_, wraps_snapshot) = WrapMap::new(tabs_snapshot, font_id, 14.0, Some(60.), cx);
let mut block_map = BlockMap::new(wraps_snapshot.clone()); let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1);
let mut writer = block_map.write(wraps_snapshot.clone(), vec![]); let mut writer = block_map.write(wraps_snapshot.clone(), vec![]);
writer.insert(vec![ writer.insert(vec![
@ -1187,8 +1194,10 @@ mod tests {
.select_font(family_id, &Default::default()) .select_font(family_id, &Default::default())
.unwrap(); .unwrap();
let font_size = 14.0; let font_size = 14.0;
let excerpt_header_height = rng.gen_range(1..=5);
log::info!("Wrap width: {:?}", wrap_width); log::info!("Wrap width: {:?}", wrap_width);
log::info!("Excerpt Header Height: {:?}", excerpt_header_height);
let buffer = if rng.gen() { let buffer = if rng.gen() {
let len = rng.gen_range(0..10); let len = rng.gen_range(0..10);
@ -1204,8 +1213,8 @@ mod tests {
let (tab_map, tabs_snapshot) = TabMap::new(folds_snapshot.clone(), tab_size); let (tab_map, tabs_snapshot) = TabMap::new(folds_snapshot.clone(), tab_size);
let (wrap_map, wraps_snapshot) = let (wrap_map, wraps_snapshot) =
WrapMap::new(tabs_snapshot, font_id, font_size, wrap_width, cx); WrapMap::new(tabs_snapshot, font_id, font_size, wrap_width, cx);
let mut block_map = BlockMap::new(wraps_snapshot); let mut block_map = BlockMap::new(wraps_snapshot, excerpt_header_height);
let mut expected_blocks = Vec::new(); let mut custom_blocks = Vec::new();
for _ in 0..operations { for _ in 0..operations {
let mut buffer_edits = Vec::new(); let mut buffer_edits = Vec::new();
@ -1258,15 +1267,15 @@ mod tests {
let mut block_map = block_map.write(wraps_snapshot, wrap_edits); let mut block_map = block_map.write(wraps_snapshot, wrap_edits);
let block_ids = block_map.insert(block_properties.clone()); let block_ids = block_map.insert(block_properties.clone());
for (block_id, props) in block_ids.into_iter().zip(block_properties) { for (block_id, props) in block_ids.into_iter().zip(block_properties) {
expected_blocks.push((block_id, props)); custom_blocks.push((block_id, props));
} }
} }
40..=59 if !expected_blocks.is_empty() => { 40..=59 if !custom_blocks.is_empty() => {
let block_count = rng.gen_range(1..=4.min(expected_blocks.len())); let block_count = rng.gen_range(1..=4.min(custom_blocks.len()));
let block_ids_to_remove = (0..block_count) let block_ids_to_remove = (0..block_count)
.map(|_| { .map(|_| {
expected_blocks custom_blocks
.remove(rng.gen_range(0..expected_blocks.len())) .remove(rng.gen_range(0..custom_blocks.len()))
.0 .0
}) })
.collect(); .collect();
@ -1304,10 +1313,8 @@ mod tests {
); );
log::info!("blocks text: {:?}", blocks_snapshot.text()); log::info!("blocks text: {:?}", blocks_snapshot.text());
let mut sorted_blocks = expected_blocks let mut expected_blocks = Vec::new();
.iter() expected_blocks.extend(custom_blocks.iter().map(|(id, block)| {
.cloned()
.map(|(id, block)| {
let mut position = block.position.to_point(&buffer_snapshot); let mut position = block.position.to_point(&buffer_snapshot);
let column = wraps_snapshot.from_point(position, Bias::Left).column(); let column = wraps_snapshot.from_point(position, Bias::Left).column();
match block.disposition { match block.disposition {
@ -1319,21 +1326,26 @@ mod tests {
} }
}; };
let row = wraps_snapshot.from_point(position, Bias::Left).row(); let row = wraps_snapshot.from_point(position, Bias::Left).row();
(row, block.disposition, *id, block.height)
}));
expected_blocks.extend(
buffer_snapshot
.excerpt_boundaries_in_range(0..buffer_snapshot.len())
.map(|boundary| {
let position =
wraps_snapshot.from_point(Point::new(boundary.row, 0), Bias::Left);
( (
id, position.row(),
BlockProperties { BlockDisposition::Above,
position: BlockPoint::new(row, column), BlockId(usize::MAX),
height: block.height, excerpt_header_height,
disposition: block.disposition,
render: block.render.clone(),
},
) )
}) }),
.collect::<Vec<_>>(); );
sorted_blocks.sort_unstable_by_key(|(id, block)| { expected_blocks.sort_unstable_by_key(|(row, disposition, id, _)| {
(block.position.row, block.disposition, Reverse(*id)) (*row, *disposition, Reverse(*id))
}); });
let mut sorted_blocks_iter = sorted_blocks.iter().peekable(); let mut sorted_blocks_iter = expected_blocks.iter().peekable();
let input_buffer_rows = buffer_snapshot.buffer_rows(0).collect::<Vec<_>>(); let input_buffer_rows = buffer_snapshot.buffer_rows(0).collect::<Vec<_>>();
let mut expected_buffer_rows = Vec::new(); let mut expected_buffer_rows = Vec::new();
@ -1350,13 +1362,13 @@ mod tests {
.to_point(WrapPoint::new(row, 0), Bias::Left) .to_point(WrapPoint::new(row, 0), Bias::Left)
.row as usize]; .row as usize];
while let Some((block_id, block)) = sorted_blocks_iter.peek() { while let Some((block_row, disposition, id, height)) = sorted_blocks_iter.peek() {
if block.position.row == row && block.disposition == BlockDisposition::Above { if *block_row == row && *disposition == BlockDisposition::Above {
expected_block_positions expected_block_positions
.push((expected_text.matches('\n').count() as u32, *block_id)); .push((expected_text.matches('\n').count() as u32, *id));
let text = "\n".repeat(block.height as usize); let text = "\n".repeat(*height as usize);
expected_text.push_str(&text); expected_text.push_str(&text);
for _ in 0..block.height { for _ in 0..*height {
expected_buffer_rows.push(None); expected_buffer_rows.push(None);
} }
sorted_blocks_iter.next(); sorted_blocks_iter.next();
@ -1369,13 +1381,13 @@ mod tests {
expected_buffer_rows.push(if soft_wrapped { None } else { buffer_row }); expected_buffer_rows.push(if soft_wrapped { None } else { buffer_row });
expected_text.push_str(input_line); expected_text.push_str(input_line);
while let Some((block_id, block)) = sorted_blocks_iter.peek() { while let Some((block_row, disposition, id, height)) = sorted_blocks_iter.peek() {
if block.position.row == row && block.disposition == BlockDisposition::Below { if *block_row == row && *disposition == BlockDisposition::Below {
expected_block_positions expected_block_positions
.push((expected_text.matches('\n').count() as u32 + 1, *block_id)); .push((expected_text.matches('\n').count() as u32 + 1, *id));
let text = "\n".repeat(block.height as usize); let text = "\n".repeat(*height as usize);
expected_text.push_str(&text); expected_text.push_str(&text);
for _ in 0..block.height { for _ in 0..*height {
expected_buffer_rows.push(None); expected_buffer_rows.push(None);
} }
sorted_blocks_iter.next(); sorted_blocks_iter.next();
@ -1409,7 +1421,14 @@ mod tests {
assert_eq!( assert_eq!(
blocks_snapshot blocks_snapshot
.blocks_in_range(0..(expected_row_count as u32)) .blocks_in_range(0..(expected_row_count as u32))
.map(|(row, block)| (row, block.id)) .map(|(row, block)| (
row,
if let Some((block, _)) = block.as_custom() {
block.id
} else {
BlockId(usize::MAX)
}
))
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
expected_block_positions expected_block_positions
); );
@ -1490,6 +1509,15 @@ mod tests {
} }
} }
impl TransformBlock {
fn as_custom(&self) -> Option<(&Block, u32)> {
match self {
TransformBlock::Custom { block, column } => Some((block, *column)),
TransformBlock::ExcerptHeader { .. } => None,
}
}
}
impl BlockSnapshot { impl BlockSnapshot {
fn to_point(&self, point: BlockPoint, bias: Bias) -> Point { fn to_point(&self, point: BlockPoint, bias: Bias) -> Point {
self.wrap_snapshot.to_point(self.to_wrap_point(point), bias) self.wrap_snapshot.to_point(self.to_wrap_point(point), bias)

View file

@ -3,7 +3,6 @@ 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;
@ -810,6 +809,7 @@ impl Editor {
settings.style.text.font_id, settings.style.text.font_id,
settings.style.text.font_size, settings.style.text.font_size,
None, None,
2,
cx, cx,
) )
}); });
@ -2604,7 +2604,11 @@ impl Editor {
.0; .0;
// Don't move lines across excerpts // Don't move lines across excerpts
if !buffer.range_contains_excerpt_boundary(insertion_point..range_to_move.end) { if buffer
.excerpt_boundaries_in_range(insertion_point..range_to_move.end)
.next()
.is_none()
{
let text = buffer let text = buffer
.text_for_range(range_to_move.clone()) .text_for_range(range_to_move.clone())
.flat_map(|s| s.chars()) .flat_map(|s| s.chars())
@ -2704,7 +2708,11 @@ impl Editor {
let insertion_point = display_map.next_line_boundary(Point::new(end_row, 0)).0; let insertion_point = display_map.next_line_boundary(Point::new(end_row, 0)).0;
// Don't move lines across excerpt boundaries // Don't move lines across excerpt boundaries
if !buffer.range_contains_excerpt_boundary(range_to_move.start..insertion_point) { if buffer
.excerpt_boundaries_in_range(range_to_move.start..insertion_point)
.next()
.is_none()
{
let mut text = String::from("\n"); let mut text = String::from("\n");
text.extend(buffer.text_for_range(range_to_move.clone())); text.extend(buffer.text_for_range(range_to_move.clone()));
text.pop(); // Drop trailing newline text.pop(); // Drop trailing newline

View file

@ -649,43 +649,44 @@ impl EditorElement {
line_layouts: &[text_layout::Line], line_layouts: &[text_layout::Line],
cx: &mut LayoutContext, cx: &mut LayoutContext,
) -> Vec<(u32, ElementBox)> { ) -> Vec<(u32, ElementBox)> {
snapshot Default::default()
.blocks_in_range(rows.clone()) // snapshot
.map(|(start_row, block)| { // .blocks_in_range(rows.clone())
let anchor_row = block // .map(|(start_row, block)| {
.position() // let anchor_row = block
.to_point(&snapshot.buffer_snapshot) // .position()
.to_display_point(snapshot) // .to_point(&snapshot.buffer_snapshot)
.row(); // .to_display_point(snapshot)
// .row();
let anchor_x = text_x // let anchor_x = text_x
+ if rows.contains(&anchor_row) { // + if rows.contains(&anchor_row) {
line_layouts[(anchor_row - rows.start) as usize] // line_layouts[(anchor_row - rows.start) as usize]
.x_for_index(block.column() as usize) // .x_for_index(block.column() as usize)
} else { // } else {
layout_line(anchor_row, snapshot, style, cx.text_layout_cache) // layout_line(anchor_row, snapshot, style, cx.text_layout_cache)
.x_for_index(block.column() as usize) // .x_for_index(block.column() as usize)
}; // };
let mut element = block.render(&BlockContext { // let mut element = block.render(&BlockContext {
cx, // cx,
anchor_x, // anchor_x,
gutter_padding, // gutter_padding,
line_height, // line_height,
scroll_x: snapshot.scroll_position.x(), // scroll_x: snapshot.scroll_position.x(),
gutter_width, // gutter_width,
em_width, // em_width,
}); // });
element.layout( // element.layout(
SizeConstraint { // SizeConstraint {
min: Vector2F::zero(), // min: Vector2F::zero(),
max: vec2f(width, block.height() as f32 * line_height), // max: vec2f(width, block.height() as f32 * line_height),
}, // },
cx, // cx,
); // );
(start_row, element) // (start_row, element)
}) // })
.collect() // .collect()
} }
} }

View file

@ -225,13 +225,8 @@ pub fn surrounding_word(map: &DisplaySnapshot, position: DisplayPoint) -> Range<
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::{ use crate::{Buffer, DisplayMap, ExcerptProperties, MultiBuffer};
display_map::{BlockDisposition, BlockProperties},
Buffer, DisplayMap, ExcerptProperties, MultiBuffer,
};
use gpui::{elements::Empty, Element};
use language::Point; use language::Point;
use std::sync::Arc;
#[gpui::test] #[gpui::test]
fn test_move_up_and_down_with_excerpts(cx: &mut gpui::MutableAppContext) { fn test_move_up_and_down_with_excerpts(cx: &mut gpui::MutableAppContext) {
@ -242,59 +237,27 @@ mod tests {
.unwrap(); .unwrap();
let buffer = cx.add_model(|cx| Buffer::new(0, "abc\ndefg\nhijkl\nmn", cx)); let buffer = cx.add_model(|cx| Buffer::new(0, "abc\ndefg\nhijkl\nmn", cx));
let mut excerpt1_header_position = None;
let mut excerpt2_header_position = None;
let multibuffer = cx.add_model(|cx| { let multibuffer = cx.add_model(|cx| {
let mut multibuffer = MultiBuffer::new(0); let mut multibuffer = MultiBuffer::new(0);
let excerpt1_id = multibuffer.push_excerpt( multibuffer.push_excerpt(
ExcerptProperties { ExcerptProperties {
buffer: &buffer, buffer: &buffer,
range: Point::new(0, 0)..Point::new(1, 4), range: Point::new(0, 0)..Point::new(1, 4),
}, },
cx, cx,
); );
let excerpt2_id = multibuffer.push_excerpt( multibuffer.push_excerpt(
ExcerptProperties { ExcerptProperties {
buffer: &buffer, buffer: &buffer,
range: Point::new(2, 0)..Point::new(3, 2), range: Point::new(2, 0)..Point::new(3, 2),
}, },
cx, cx,
); );
excerpt1_header_position = Some(
multibuffer
.read(cx)
.anchor_in_excerpt(excerpt1_id, language::Anchor::min()),
);
excerpt2_header_position = Some(
multibuffer
.read(cx)
.anchor_in_excerpt(excerpt2_id, language::Anchor::min()),
);
multibuffer multibuffer
}); });
let display_map = let display_map =
cx.add_model(|cx| DisplayMap::new(multibuffer, 2, font_id, 14.0, None, cx)); cx.add_model(|cx| DisplayMap::new(multibuffer, 2, font_id, 14.0, None, 2, cx));
display_map.update(cx, |display_map, cx| {
display_map.insert_blocks(
[
BlockProperties {
position: excerpt1_header_position.unwrap(),
height: 2,
render: Arc::new(|_| Empty::new().boxed()),
disposition: BlockDisposition::Above,
},
BlockProperties {
position: excerpt2_header_position.unwrap(),
height: 3,
render: Arc::new(|_| Empty::new().boxed()),
disposition: BlockDisposition::Above,
},
],
cx,
)
});
let snapshot = display_map.update(cx, |map, cx| map.snapshot(cx)); let snapshot = display_map.update(cx, |map, cx| map.snapshot(cx));
assert_eq!(snapshot.text(), "\n\nabc\ndefg\n\n\n\nhijkl\nmn"); assert_eq!(snapshot.text(), "\n\nabc\ndefg\n\n\n\nhijkl\nmn");
@ -352,7 +315,7 @@ mod tests {
let buffer = MultiBuffer::build_simple("a bcΔ defγ hi—jk", cx); let buffer = MultiBuffer::build_simple("a bcΔ defγ hi—jk", cx);
let display_map = let display_map =
cx.add_model(|cx| DisplayMap::new(buffer, tab_size, font_id, font_size, None, cx)); cx.add_model(|cx| DisplayMap::new(buffer, tab_size, font_id, font_size, None, 1, cx));
let snapshot = display_map.update(cx, |map, cx| map.snapshot(cx)); let snapshot = display_map.update(cx, |map, cx| map.snapshot(cx));
assert_eq!( assert_eq!(
prev_word_boundary(&snapshot, DisplayPoint::new(0, 12)), prev_word_boundary(&snapshot, DisplayPoint::new(0, 12)),
@ -408,7 +371,7 @@ mod tests {
let font_size = 14.0; let font_size = 14.0;
let buffer = MultiBuffer::build_simple("lorem ipsum dolor\n sit", cx); let buffer = MultiBuffer::build_simple("lorem ipsum dolor\n sit", cx);
let display_map = let display_map =
cx.add_model(|cx| DisplayMap::new(buffer, tab_size, font_id, font_size, None, cx)); cx.add_model(|cx| DisplayMap::new(buffer, tab_size, font_id, font_size, None, 1, cx));
let snapshot = display_map.update(cx, |map, cx| map.snapshot(cx)); let snapshot = display_map.update(cx, |map, cx| map.snapshot(cx));
assert_eq!( assert_eq!(

View file

@ -15,7 +15,6 @@ 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},
@ -83,6 +82,7 @@ struct BufferState {
last_parse_count: usize, last_parse_count: usize,
last_selections_update_count: usize, last_selections_update_count: usize,
last_diagnostics_update_count: usize, last_diagnostics_update_count: usize,
last_file_update_count: usize,
excerpts: Vec<ExcerptId>, excerpts: Vec<ExcerptId>,
_subscriptions: [gpui::Subscription; 2], _subscriptions: [gpui::Subscription; 2],
} }
@ -105,7 +105,6 @@ pub struct ExcerptProperties<'a, T> {
pub struct ExcerptBoundary { pub struct ExcerptBoundary {
pub row: u32, pub row: u32,
pub buffer: BufferSnapshot, pub buffer: BufferSnapshot,
pub path: Option<Arc<Path>>,
pub range: Range<text::Anchor>, pub range: Range<text::Anchor>,
pub starts_new_buffer: bool, pub starts_new_buffer: bool,
} }
@ -679,6 +678,7 @@ impl MultiBuffer {
last_parse_count: buffer_snapshot.parse_count(), last_parse_count: buffer_snapshot.parse_count(),
last_selections_update_count: buffer_snapshot.selections_update_count(), last_selections_update_count: buffer_snapshot.selections_update_count(),
last_diagnostics_update_count: buffer_snapshot.diagnostics_update_count(), last_diagnostics_update_count: buffer_snapshot.diagnostics_update_count(),
last_file_update_count: buffer_snapshot.file_update_count(),
excerpts: Default::default(), excerpts: Default::default(),
_subscriptions: [ _subscriptions: [
cx.observe(&props.buffer, |_, _, cx| cx.notify()), cx.observe(&props.buffer, |_, _, cx| cx.notify()),
@ -929,6 +929,7 @@ impl MultiBuffer {
let parse_count = buffer.parse_count(); let parse_count = buffer.parse_count();
let selections_update_count = buffer.selections_update_count(); let selections_update_count = buffer.selections_update_count();
let diagnostics_update_count = buffer.diagnostics_update_count(); let diagnostics_update_count = buffer.diagnostics_update_count();
let file_update_count = buffer.file_update_count();
let buffer_edited = version.changed_since(&buffer_state.last_version); let buffer_edited = version.changed_since(&buffer_state.last_version);
let buffer_reparsed = parse_count > buffer_state.last_parse_count; let buffer_reparsed = parse_count > buffer_state.last_parse_count;
@ -936,15 +937,18 @@ impl MultiBuffer {
selections_update_count > buffer_state.last_selections_update_count; selections_update_count > buffer_state.last_selections_update_count;
let buffer_diagnostics_updated = let buffer_diagnostics_updated =
diagnostics_update_count > buffer_state.last_diagnostics_update_count; diagnostics_update_count > buffer_state.last_diagnostics_update_count;
let buffer_file_updated = file_update_count > buffer_state.last_file_update_count;
if buffer_edited if buffer_edited
|| buffer_reparsed || buffer_reparsed
|| buffer_selections_updated || buffer_selections_updated
|| buffer_diagnostics_updated || buffer_diagnostics_updated
|| buffer_file_updated
{ {
buffer_state.last_version = version; buffer_state.last_version = version;
buffer_state.last_parse_count = parse_count; buffer_state.last_parse_count = parse_count;
buffer_state.last_selections_update_count = selections_update_count; buffer_state.last_selections_update_count = selections_update_count;
buffer_state.last_diagnostics_update_count = diagnostics_update_count; buffer_state.last_diagnostics_update_count = diagnostics_update_count;
buffer_state.last_file_update_count = file_update_count;
excerpts_to_edit.extend( excerpts_to_edit.extend(
buffer_state buffer_state
.excerpts .excerpts
@ -1761,38 +1765,35 @@ impl MultiBufferSnapshot {
} }
} }
pub fn range_contains_excerpt_boundary<T: ToOffset>(&self, range: Range<T>) -> bool {
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 start_id = cursor
.item()
.or_else(|| cursor.prev_item())
.map(|excerpt| &excerpt.id);
cursor.seek_forward(&end, Bias::Right, &());
let end_id = cursor
.item()
.or_else(|| cursor.prev_item())
.map(|excerpt| &excerpt.id);
start_id != end_id
}
pub fn excerpt_boundaries_in_range<'a, T: ToOffset>( pub fn excerpt_boundaries_in_range<'a, T: ToOffset>(
&'a self, &'a self,
range: Range<T>, range: Range<T>,
) -> impl Iterator<Item = ExcerptBoundary> + 'a { ) -> impl Iterator<Item = ExcerptBoundary> + 'a {
let start = range.start.to_offset(self); let start = range.start.to_offset(self);
let end = range.end.to_offset(self); let end = range.end.to_offset(self);
let mut cursor = self.excerpts.cursor::<(usize, Option<&ExcerptId>)>(); let mut cursor = self
.excerpts
.cursor::<(usize, (Option<&ExcerptId>, Point))>();
cursor.seek(&start, Bias::Right, &()); cursor.seek(&start, Bias::Right, &());
let prev_buffer_id = cursor.prev_item().map(|excerpt| excerpt.buffer_id); let mut prev_buffer_id = cursor.prev_item().map(|excerpt| excerpt.buffer_id);
std::iter::from_fn(move || { std::iter::from_fn(move || {
if start <= cursor.start().0 && end > cursor.start().0 {
let excerpt = cursor.item()?; let excerpt = cursor.item()?;
let starts_new_buffer = Some(excerpt.buffer_id) != prev_buffer_id; let starts_new_buffer = Some(excerpt.buffer_id) != prev_buffer_id;
todo!() let boundary = ExcerptBoundary {
row: cursor.start().1 .1.row,
buffer: excerpt.buffer.clone(),
range: excerpt.range.clone(),
starts_new_buffer,
};
prev_buffer_id = Some(excerpt.buffer_id);
cursor.next(&());
Some(boundary)
} else {
None
}
}) })
} }
@ -2648,21 +2649,50 @@ mod tests {
); );
assert_eq!(snapshot.buffer_rows(4).collect::<Vec<_>>(), [Some(3)]); assert_eq!(snapshot.buffer_rows(4).collect::<Vec<_>>(), [Some(3)]);
assert_eq!(snapshot.buffer_rows(5).collect::<Vec<_>>(), []); assert_eq!(snapshot.buffer_rows(5).collect::<Vec<_>>(), []);
assert!(!snapshot.range_contains_excerpt_boundary(Point::new(1, 0)..Point::new(1, 5))); assert!(snapshot
assert!(snapshot.range_contains_excerpt_boundary(Point::new(1, 0)..Point::new(2, 0))); .excerpt_boundaries_in_range(Point::new(1, 0)..Point::new(1, 5))
assert!(snapshot.range_contains_excerpt_boundary(Point::new(1, 0)..Point::new(4, 0))); .next()
assert!(!snapshot.range_contains_excerpt_boundary(Point::new(2, 0)..Point::new(3, 0))); .is_none());
assert!(!snapshot.range_contains_excerpt_boundary(Point::new(4, 0)..Point::new(4, 2))); assert!(snapshot
assert!(!snapshot.range_contains_excerpt_boundary(Point::new(4, 2)..Point::new(4, 2))); .excerpt_boundaries_in_range(Point::new(1, 0)..Point::new(2, 0))
.next()
.is_some());
assert!(snapshot
.excerpt_boundaries_in_range(Point::new(1, 0)..Point::new(4, 0))
.next()
.is_some());
assert!(snapshot
.excerpt_boundaries_in_range(Point::new(2, 0)..Point::new(3, 0))
.next()
.is_none());
assert!(snapshot
.excerpt_boundaries_in_range(Point::new(4, 0)..Point::new(4, 2))
.next()
.is_none());
assert!(snapshot
.excerpt_boundaries_in_range(Point::new(4, 2)..Point::new(4, 2))
.next()
.is_none());
assert_eq!( assert_eq!(
snapshot snapshot
.excerpt_boundaries_in_range(Point::new(0, 0)..Point::new(4, 2)) .excerpt_boundaries_in_range(Point::new(0, 0)..Point::new(4, 2))
.map(|boundary| (
boundary.row,
boundary
.buffer
.text_for_range(boundary.range)
.collect::<String>(),
boundary.starts_new_buffer
))
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
&[ &[
(Some(buffer_1.clone()), true), (0, "".to_string(), true),
(Some(buffer_1.clone()), false), (0, "".to_string(), true),
(Some(buffer_2.clone()), false), (0, "".to_string(), true),
(0, "".to_string(), true),
(0, "".to_string(), true),
(0, "".to_string(), true),
] ]
); );

View file

@ -68,6 +68,7 @@ pub struct Buffer {
remote_selections: TreeMap<ReplicaId, SelectionSet>, remote_selections: TreeMap<ReplicaId, SelectionSet>,
selections_update_count: usize, selections_update_count: usize,
diagnostics_update_count: usize, diagnostics_update_count: usize,
file_update_count: usize,
language_server: Option<LanguageServerState>, language_server: Option<LanguageServerState>,
completion_triggers: Vec<String>, completion_triggers: Vec<String>,
deferred_ops: OperationQueue<Operation>, deferred_ops: OperationQueue<Operation>,
@ -78,8 +79,10 @@ pub struct Buffer {
pub struct BufferSnapshot { pub struct BufferSnapshot {
text: text::BufferSnapshot, text: text::BufferSnapshot,
tree: Option<Tree>, tree: Option<Tree>,
path: Option<Arc<Path>>,
diagnostics: DiagnosticSet, diagnostics: DiagnosticSet,
diagnostics_update_count: usize, diagnostics_update_count: usize,
file_update_count: usize,
remote_selections: TreeMap<ReplicaId, SelectionSet>, remote_selections: TreeMap<ReplicaId, SelectionSet>,
selections_update_count: usize, selections_update_count: usize,
is_parsing: bool, is_parsing: bool,
@ -498,6 +501,7 @@ impl Buffer {
selections_update_count: 0, selections_update_count: 0,
diagnostics: Default::default(), diagnostics: Default::default(),
diagnostics_update_count: 0, diagnostics_update_count: 0,
file_update_count: 0,
language_server: None, language_server: None,
completion_triggers: Default::default(), completion_triggers: Default::default(),
deferred_ops: OperationQueue::new(), deferred_ops: OperationQueue::new(),
@ -510,9 +514,11 @@ impl Buffer {
BufferSnapshot { BufferSnapshot {
text: self.text.snapshot(), text: self.text.snapshot(),
tree: self.syntax_tree(), tree: self.syntax_tree(),
path: self.file.as_ref().map(|f| f.path().clone()),
remote_selections: self.remote_selections.clone(), remote_selections: self.remote_selections.clone(),
diagnostics: self.diagnostics.clone(), diagnostics: self.diagnostics.clone(),
diagnostics_update_count: self.diagnostics_update_count, diagnostics_update_count: self.diagnostics_update_count,
file_update_count: self.file_update_count,
is_parsing: self.parsing_in_background, is_parsing: self.parsing_in_background,
language: self.language.clone(), language: self.language.clone(),
parse_count: self.parse_count, parse_count: self.parse_count,
@ -722,6 +728,7 @@ impl Buffer {
self.saved_version = version; self.saved_version = version;
if let Some(new_file) = new_file { if let Some(new_file) = new_file {
self.file = Some(new_file); self.file = Some(new_file);
self.file_update_count += 1;
} }
if let Some((state, local_file)) = &self if let Some((state, local_file)) = &self
.language_server .language_server
@ -744,6 +751,7 @@ impl Buffer {
.detach() .detach()
} }
cx.emit(Event::Saved); cx.emit(Event::Saved);
cx.notify();
} }
pub fn did_reload( pub fn did_reload(
@ -819,7 +827,9 @@ impl Buffer {
} }
if file_changed { if file_changed {
self.file_update_count += 1;
cx.emit(Event::FileHandleChanged); cx.emit(Event::FileHandleChanged);
cx.notify();
} }
self.file = Some(new_file); self.file = Some(new_file);
task task
@ -849,6 +859,10 @@ impl Buffer {
self.diagnostics_update_count self.diagnostics_update_count
} }
pub fn file_update_count(&self) -> usize {
self.file_update_count
}
pub(crate) fn syntax_tree(&self) -> Option<Tree> { pub(crate) fn syntax_tree(&self) -> Option<Tree> {
if let Some(syntax_tree) = self.syntax_tree.lock().as_mut() { if let Some(syntax_tree) = self.syntax_tree.lock().as_mut() {
self.interpolate_tree(syntax_tree); self.interpolate_tree(syntax_tree);
@ -2231,6 +2245,14 @@ impl BufferSnapshot {
pub fn selections_update_count(&self) -> usize { pub fn selections_update_count(&self) -> usize {
self.selections_update_count self.selections_update_count
} }
pub fn path(&self) -> Option<&Arc<Path>> {
self.path.as_ref()
}
pub fn file_update_count(&self) -> usize {
self.file_update_count
}
} }
impl Clone for BufferSnapshot { impl Clone for BufferSnapshot {
@ -2238,10 +2260,12 @@ impl Clone for BufferSnapshot {
Self { Self {
text: self.text.clone(), text: self.text.clone(),
tree: self.tree.clone(), tree: self.tree.clone(),
path: self.path.clone(),
remote_selections: self.remote_selections.clone(), remote_selections: self.remote_selections.clone(),
diagnostics: self.diagnostics.clone(), diagnostics: self.diagnostics.clone(),
selections_update_count: self.selections_update_count, selections_update_count: self.selections_update_count,
diagnostics_update_count: self.diagnostics_update_count, diagnostics_update_count: self.diagnostics_update_count,
file_update_count: self.file_update_count,
is_parsing: self.is_parsing, is_parsing: self.is_parsing,
language: self.language.clone(), language: self.language.clone(),
parse_count: self.parse_count, parse_count: self.parse_count,