From 0e1318dfe4b37f05361928e135b0f7bcbf09184c Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 9 Feb 2022 11:09:11 +0100 Subject: [PATCH] WIP: Make `editor` crate compile again Tests are still failing though. --- crates/editor/src/display_map.rs | 45 +++-- crates/editor/src/display_map/block_map.rs | 220 ++++++++++++--------- crates/editor/src/editor.rs | 14 +- crates/editor/src/element.rs | 75 +++---- crates/editor/src/movement.rs | 49 +---- crates/editor/src/multi_buffer.rs | 98 +++++---- crates/language/src/buffer.rs | 24 +++ 7 files changed, 299 insertions(+), 226 deletions(-) diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index 697dc5ea6256f29a0bab8eb88b5d01bfdb7eff81..4659d66e562045888aff24f97199b378f6bdfe26 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -15,8 +15,8 @@ use tab_map::TabMap; use wrap_map::WrapMap; pub use block_map::{ - AlignedBlock, BlockBufferRows as DisplayBufferRows, BlockChunks as DisplayChunks, BlockContext, - BlockDisposition, BlockId, BlockProperties, RenderBlock, + BlockBufferRows as DisplayBufferRows, BlockChunks as DisplayChunks, BlockContext, + BlockDisposition, BlockId, BlockProperties, RenderBlock, TransformBlock, }; pub trait ToDisplayPoint { @@ -43,13 +43,14 @@ impl DisplayMap { font_id: FontId, font_size: f32, wrap_width: Option, + excerpt_header_height: u8, cx: &mut ModelContext, ) -> Self { let buffer_subscription = buffer.update(cx, |buffer, _| buffer.subscribe()); let (fold_map, snapshot) = FoldMap::new(buffer.read(cx).snapshot(cx)); let (tab_map, snapshot) = TabMap::new(snapshot, tab_size); 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(); DisplayMap { buffer, @@ -318,7 +319,7 @@ impl DisplaySnapshot { pub fn blocks_in_range<'a>( &'a self, rows: Range, - ) -> impl Iterator { + ) -> impl Iterator { self.blocks_snapshot.blocks_in_range(rows) } @@ -471,6 +472,7 @@ mod tests { let font_cache = cx.font_cache().clone(); 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 font_id = font_cache .select_font(family_id, &Default::default()) @@ -497,7 +499,15 @@ mod tests { }); 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 fold_count = 0; @@ -711,7 +721,15 @@ mod tests { let text = "one two three four five\nsix seven eight"; let buffer = MultiBuffer::build_simple(text, 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)); @@ -791,7 +809,7 @@ mod tests { .unwrap(); let font_size = 14.0; 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.edit( @@ -871,7 +889,7 @@ mod tests { let font_size = 14.0; 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!( cx.update(|cx| chunks(0..5, &map, &theme, cx)), vec![ @@ -958,8 +976,9 @@ mod tests { .unwrap(); let font_size = 16.0; - let map = cx - .add_model(|cx| DisplayMap::new(buffer, tab_size, font_id, font_size, Some(40.0), cx)); + let map = cx.add_model(|cx| { + DisplayMap::new(buffer, tab_size, font_id, font_size, Some(40.0), 1, cx) + }); assert_eq!( cx.update(|cx| chunks(0..5, &map, &theme, cx)), [ @@ -1003,7 +1022,7 @@ mod tests { .unwrap(); let font_size = 14.0; 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)); @@ -1047,7 +1066,7 @@ mod tests { let font_size = 14.0; 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)); assert_eq!(map.text(), "✅ α\nβ \n🏀β γ"); @@ -1105,7 +1124,7 @@ mod tests { .unwrap(); let font_size = 14.0; 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!( map.update(cx, |map, cx| map.snapshot(cx)).max_point(), diff --git a/crates/editor/src/display_map/block_map.rs b/crates/editor/src/display_map/block_map.rs index 72bda019318ed692b6d478639cf107a75484859e..4bf86ca2828fd064fbf92873e300b4665bb12267 100644 --- a/crates/editor/src/display_map/block_map.rs +++ b/crates/editor/src/display_map/block_map.rs @@ -5,10 +5,9 @@ use gpui::{AppContext, ElementBox}; use language::{BufferSnapshot, Chunk}; use parking_lot::Mutex; use std::{ - cmp::{self, Ordering, Reverse}, + cmp::{self, Ordering}, fmt::Debug, ops::{Deref, Range}, - path::Path, sync::{ atomic::{AtomicUsize, Ordering::SeqCst}, Arc, @@ -24,6 +23,7 @@ pub struct BlockMap { wrap_snapshot: Mutex, blocks: Vec>, transforms: Mutex>, + excerpt_header_height: u8, } pub struct BlockMapWriter<'a>(&'a mut BlockMap); @@ -89,7 +89,7 @@ struct Transform { } #[derive(Clone)] -enum TransformBlock { +pub enum TransformBlock { Custom { block: Arc, column: u32, @@ -97,17 +97,24 @@ enum TransformBlock { ExcerptHeader { buffer: BufferSnapshot, range: Range, - path: Option>, + height: u8, }, } impl TransformBlock { fn disposition(&self) -> BlockDisposition { match self { - TransformBlock::Custom { block, column } => block.disposition, + TransformBlock::Custom { block, .. } => block.disposition, TransformBlock::ExcerptHeader { .. } => BlockDisposition::Above, } } + + fn height(&self) -> u8 { + match self { + TransformBlock::Custom { block, .. } => block.height, + TransformBlock::ExcerptHeader { height, .. } => *height, + } + } } impl Debug for TransformBlock { @@ -118,9 +125,10 @@ impl Debug for TransformBlock { .field("block", block) .field("column", column) .finish(), - Self::ExcerptHeader { buffer, path, .. } => { - f.debug_struct("ExcerptHeader").field("path", path).finish() - } + Self::ExcerptHeader { buffer, .. } => f + .debug_struct("ExcerptHeader") + .field("path", &buffer.path()) + .finish(), } } } @@ -147,7 +155,7 @@ pub struct BlockBufferRows<'a> { } impl BlockMap { - pub fn new(wrap_snapshot: WrapSnapshot) -> Self { + pub fn new(wrap_snapshot: WrapSnapshot, excerpt_header_height: u8) -> Self { Self { next_block_id: AtomicUsize::new(0), blocks: Vec::new(), @@ -156,6 +164,7 @@ impl BlockMap { &(), )), wrap_snapshot: Mutex::new(wrap_snapshot), + excerpt_header_height, } } @@ -202,7 +211,7 @@ impl BlockMap { if transform .block .as_ref() - .map_or(false, |b| b.disposition.is_below()) + .map_or(false, |b| b.disposition().is_below()) { new_transforms.push(transform.clone(), &()); cursor.next(&()); @@ -227,7 +236,7 @@ impl BlockMap { if transform .block .as_ref() - .map_or(false, |b| b.disposition.is_below()) + .map_or(false, |b| b.disposition().is_below()) { cursor.next(&()); } else { @@ -248,7 +257,7 @@ impl BlockMap { if transform .block .as_ref() - .map_or(false, |b| b.disposition.is_below()) + .map_or(false, |b| b.disposition().is_below()) { cursor.next(&()); } else { @@ -328,7 +337,7 @@ impl BlockMap { TransformBlock::ExcerptHeader { buffer: excerpt_boundary.buffer, 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 // 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, // and then insert the block itself. @@ -577,7 +602,7 @@ impl BlockSnapshot { pub fn blocks_in_range<'a>( &'a self, rows: Range, - ) -> impl Iterator { + ) -> impl Iterator { let mut cursor = self.transforms.cursor::(); cursor.seek(&BlockRow(rows.start), Bias::Right, &()); std::iter::from_fn(move || { @@ -697,7 +722,7 @@ impl BlockSnapshot { let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(); cursor.seek(&BlockRow(block_point.row), Bias::Right, &()); 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::Below) => { let wrap_row = cursor.start().1 .0 - 1; @@ -726,13 +751,13 @@ impl Transform { } } - fn block(block: Arc, column: u32) -> Self { + fn block(block: TransformBlock) -> Self { Self { summary: TransformSummary { 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> { 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 { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("Block") @@ -931,6 +936,7 @@ mod tests { use crate::multi_buffer::MultiBuffer; use gpui::{elements::Empty, Element}; use rand::prelude::*; + use std::cmp::Reverse; use std::env; use text::RandomCharIter; @@ -964,7 +970,7 @@ mod tests { let (fold_map, folds_snapshot) = FoldMap::new(buffer_snapshot.clone()); 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 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![]); writer.insert(vec![ @@ -994,9 +1000,10 @@ mod tests { let blocks = snapshot .blocks_in_range(0..8) .map(|(start_row, block)| { + let (block, column) = block.as_custom().unwrap(); ( - start_row..start_row + block.height(), - block.column(), + start_row..start_row + block.height as u32, + column, block .render(&BlockContext { cx, @@ -1142,7 +1149,7 @@ mod tests { let (_, folds_snapshot) = FoldMap::new(buffer_snapshot.clone()); let (_, tabs_snapshot) = TabMap::new(folds_snapshot.clone(), 1); 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![]); writer.insert(vec![ @@ -1187,8 +1194,10 @@ mod tests { .select_font(family_id, &Default::default()) .unwrap(); let font_size = 14.0; + let excerpt_header_height = rng.gen_range(1..=5); log::info!("Wrap width: {:?}", wrap_width); + log::info!("Excerpt Header Height: {:?}", excerpt_header_height); let buffer = if rng.gen() { 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 (wrap_map, wraps_snapshot) = WrapMap::new(tabs_snapshot, font_id, font_size, wrap_width, cx); - let mut block_map = BlockMap::new(wraps_snapshot); - let mut expected_blocks = Vec::new(); + let mut block_map = BlockMap::new(wraps_snapshot, excerpt_header_height); + let mut custom_blocks = Vec::new(); for _ in 0..operations { let mut buffer_edits = Vec::new(); @@ -1258,15 +1267,15 @@ mod tests { let mut block_map = block_map.write(wraps_snapshot, wrap_edits); let block_ids = block_map.insert(block_properties.clone()); 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() => { - let block_count = rng.gen_range(1..=4.min(expected_blocks.len())); + 40..=59 if !custom_blocks.is_empty() => { + let block_count = rng.gen_range(1..=4.min(custom_blocks.len())); let block_ids_to_remove = (0..block_count) .map(|_| { - expected_blocks - .remove(rng.gen_range(0..expected_blocks.len())) + custom_blocks + .remove(rng.gen_range(0..custom_blocks.len())) .0 }) .collect(); @@ -1304,36 +1313,39 @@ mod tests { ); log::info!("blocks text: {:?}", blocks_snapshot.text()); - let mut sorted_blocks = expected_blocks - .iter() - .cloned() - .map(|(id, block)| { - let mut position = block.position.to_point(&buffer_snapshot); - let column = wraps_snapshot.from_point(position, Bias::Left).column(); - match block.disposition { - BlockDisposition::Above => { - position.column = 0; - } - BlockDisposition::Below => { - position.column = buffer_snapshot.line_len(position.row); - } - }; - let row = wraps_snapshot.from_point(position, Bias::Left).row(); - ( - id, - BlockProperties { - position: BlockPoint::new(row, column), - height: block.height, - disposition: block.disposition, - render: block.render.clone(), - }, - ) - }) - .collect::>(); - sorted_blocks.sort_unstable_by_key(|(id, block)| { - (block.position.row, block.disposition, Reverse(*id)) + let mut expected_blocks = Vec::new(); + expected_blocks.extend(custom_blocks.iter().map(|(id, block)| { + let mut position = block.position.to_point(&buffer_snapshot); + let column = wraps_snapshot.from_point(position, Bias::Left).column(); + match block.disposition { + BlockDisposition::Above => { + position.column = 0; + } + BlockDisposition::Below => { + position.column = buffer_snapshot.line_len(position.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); + ( + position.row(), + BlockDisposition::Above, + BlockId(usize::MAX), + excerpt_header_height, + ) + }), + ); + expected_blocks.sort_unstable_by_key(|(row, disposition, 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::>(); let mut expected_buffer_rows = Vec::new(); @@ -1350,13 +1362,13 @@ mod tests { .to_point(WrapPoint::new(row, 0), Bias::Left) .row as usize]; - while let Some((block_id, block)) = sorted_blocks_iter.peek() { - if block.position.row == row && block.disposition == BlockDisposition::Above { + while let Some((block_row, disposition, id, height)) = sorted_blocks_iter.peek() { + if *block_row == row && *disposition == BlockDisposition::Above { expected_block_positions - .push((expected_text.matches('\n').count() as u32, *block_id)); - let text = "\n".repeat(block.height as usize); + .push((expected_text.matches('\n').count() as u32, *id)); + let text = "\n".repeat(*height as usize); expected_text.push_str(&text); - for _ in 0..block.height { + for _ in 0..*height { expected_buffer_rows.push(None); } sorted_blocks_iter.next(); @@ -1369,13 +1381,13 @@ mod tests { expected_buffer_rows.push(if soft_wrapped { None } else { buffer_row }); expected_text.push_str(input_line); - while let Some((block_id, block)) = sorted_blocks_iter.peek() { - if block.position.row == row && block.disposition == BlockDisposition::Below { + while let Some((block_row, disposition, id, height)) = sorted_blocks_iter.peek() { + if *block_row == row && *disposition == BlockDisposition::Below { expected_block_positions - .push((expected_text.matches('\n').count() as u32 + 1, *block_id)); - let text = "\n".repeat(block.height as usize); + .push((expected_text.matches('\n').count() as u32 + 1, *id)); + let text = "\n".repeat(*height as usize); expected_text.push_str(&text); - for _ in 0..block.height { + for _ in 0..*height { expected_buffer_rows.push(None); } sorted_blocks_iter.next(); @@ -1409,7 +1421,14 @@ mod tests { assert_eq!( blocks_snapshot .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::>(), 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 { fn to_point(&self, point: BlockPoint, bias: Bias) -> Point { self.wrap_snapshot.to_point(self.to_wrap_point(point), bias) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index c6a1544823f545b6239d4b544dc36dcb827347af..35c7d9f089cec910946b793716b3e2944bc4215d 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -3,7 +3,6 @@ mod element; pub mod items; pub mod movement; mod multi_buffer; -mod multi_editor; #[cfg(test)] mod test; @@ -810,6 +809,7 @@ impl Editor { settings.style.text.font_id, settings.style.text.font_size, None, + 2, cx, ) }); @@ -2604,7 +2604,11 @@ impl Editor { .0; // 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 .text_for_range(range_to_move.clone()) .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; // 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"); text.extend(buffer.text_for_range(range_to_move.clone())); text.pop(); // Drop trailing newline diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 1f347b09a5d23f9c311bc2e02592d4bc8257bc7a..2f391aa9a9e4548d3f8a0b83215562969ab7df9a 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -649,43 +649,44 @@ impl EditorElement { line_layouts: &[text_layout::Line], cx: &mut LayoutContext, ) -> Vec<(u32, ElementBox)> { - snapshot - .blocks_in_range(rows.clone()) - .map(|(start_row, block)| { - let anchor_row = block - .position() - .to_point(&snapshot.buffer_snapshot) - .to_display_point(snapshot) - .row(); - - let anchor_x = text_x - + if rows.contains(&anchor_row) { - line_layouts[(anchor_row - rows.start) as usize] - .x_for_index(block.column() as usize) - } else { - layout_line(anchor_row, snapshot, style, cx.text_layout_cache) - .x_for_index(block.column() as usize) - }; - - let mut element = block.render(&BlockContext { - cx, - anchor_x, - gutter_padding, - line_height, - scroll_x: snapshot.scroll_position.x(), - gutter_width, - em_width, - }); - element.layout( - SizeConstraint { - min: Vector2F::zero(), - max: vec2f(width, block.height() as f32 * line_height), - }, - cx, - ); - (start_row, element) - }) - .collect() + Default::default() + // snapshot + // .blocks_in_range(rows.clone()) + // .map(|(start_row, block)| { + // let anchor_row = block + // .position() + // .to_point(&snapshot.buffer_snapshot) + // .to_display_point(snapshot) + // .row(); + + // let anchor_x = text_x + // + if rows.contains(&anchor_row) { + // line_layouts[(anchor_row - rows.start) as usize] + // .x_for_index(block.column() as usize) + // } else { + // layout_line(anchor_row, snapshot, style, cx.text_layout_cache) + // .x_for_index(block.column() as usize) + // }; + + // let mut element = block.render(&BlockContext { + // cx, + // anchor_x, + // gutter_padding, + // line_height, + // scroll_x: snapshot.scroll_position.x(), + // gutter_width, + // em_width, + // }); + // element.layout( + // SizeConstraint { + // min: Vector2F::zero(), + // max: vec2f(width, block.height() as f32 * line_height), + // }, + // cx, + // ); + // (start_row, element) + // }) + // .collect() } } diff --git a/crates/editor/src/movement.rs b/crates/editor/src/movement.rs index 287ad34dc87e6687e7041f027b93ed205621ef99..f4047a4cf5aff8645a4d13fb9111ee67da742786 100644 --- a/crates/editor/src/movement.rs +++ b/crates/editor/src/movement.rs @@ -225,13 +225,8 @@ pub fn surrounding_word(map: &DisplaySnapshot, position: DisplayPoint) -> Range< #[cfg(test)] mod tests { use super::*; - use crate::{ - display_map::{BlockDisposition, BlockProperties}, - Buffer, DisplayMap, ExcerptProperties, MultiBuffer, - }; - use gpui::{elements::Empty, Element}; + use crate::{Buffer, DisplayMap, ExcerptProperties, MultiBuffer}; use language::Point; - use std::sync::Arc; #[gpui::test] fn test_move_up_and_down_with_excerpts(cx: &mut gpui::MutableAppContext) { @@ -242,59 +237,27 @@ mod tests { .unwrap(); 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 mut multibuffer = MultiBuffer::new(0); - let excerpt1_id = multibuffer.push_excerpt( + multibuffer.push_excerpt( ExcerptProperties { buffer: &buffer, range: Point::new(0, 0)..Point::new(1, 4), }, cx, ); - let excerpt2_id = multibuffer.push_excerpt( + multibuffer.push_excerpt( ExcerptProperties { buffer: &buffer, range: Point::new(2, 0)..Point::new(3, 2), }, 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 }); let display_map = - cx.add_model(|cx| DisplayMap::new(multibuffer, 2, font_id, 14.0, None, 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, - ) - }); + cx.add_model(|cx| DisplayMap::new(multibuffer, 2, font_id, 14.0, None, 2, cx)); let snapshot = display_map.update(cx, |map, cx| map.snapshot(cx)); 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 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)); assert_eq!( prev_word_boundary(&snapshot, DisplayPoint::new(0, 12)), @@ -408,7 +371,7 @@ mod tests { let font_size = 14.0; let buffer = MultiBuffer::build_simple("lorem ipsum dolor\n sit", cx); 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)); assert_eq!( diff --git a/crates/editor/src/multi_buffer.rs b/crates/editor/src/multi_buffer.rs index 89404cd70e6fe49986e263be5dc4d4face0986b9..32127341ff6096a36e66645d254bb2009a94a563 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -15,7 +15,6 @@ use std::{ cmp, fmt, io, iter::{self, FromIterator}, ops::{Range, Sub}, - path::Path, str, sync::Arc, time::{Duration, Instant}, @@ -83,6 +82,7 @@ struct BufferState { last_parse_count: usize, last_selections_update_count: usize, last_diagnostics_update_count: usize, + last_file_update_count: usize, excerpts: Vec, _subscriptions: [gpui::Subscription; 2], } @@ -105,7 +105,6 @@ pub struct ExcerptProperties<'a, T> { pub struct ExcerptBoundary { pub row: u32, pub buffer: BufferSnapshot, - pub path: Option>, pub range: Range, pub starts_new_buffer: bool, } @@ -679,6 +678,7 @@ impl MultiBuffer { last_parse_count: buffer_snapshot.parse_count(), last_selections_update_count: buffer_snapshot.selections_update_count(), last_diagnostics_update_count: buffer_snapshot.diagnostics_update_count(), + last_file_update_count: buffer_snapshot.file_update_count(), excerpts: Default::default(), _subscriptions: [ cx.observe(&props.buffer, |_, _, cx| cx.notify()), @@ -929,6 +929,7 @@ impl MultiBuffer { let parse_count = buffer.parse_count(); let selections_update_count = buffer.selections_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_reparsed = parse_count > buffer_state.last_parse_count; @@ -936,15 +937,18 @@ impl MultiBuffer { selections_update_count > buffer_state.last_selections_update_count; let buffer_diagnostics_updated = 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 || buffer_reparsed || buffer_selections_updated || buffer_diagnostics_updated + || buffer_file_updated { buffer_state.last_version = version; buffer_state.last_parse_count = parse_count; buffer_state.last_selections_update_count = selections_update_count; buffer_state.last_diagnostics_update_count = diagnostics_update_count; + buffer_state.last_file_update_count = file_update_count; excerpts_to_edit.extend( buffer_state .excerpts @@ -1761,38 +1765,35 @@ impl MultiBufferSnapshot { } } - pub fn range_contains_excerpt_boundary(&self, range: Range) -> 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>( &'a self, range: Range, ) -> impl Iterator + 'a { let start = range.start.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, &()); - 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 || { - let excerpt = cursor.item()?; - let starts_new_buffer = Some(excerpt.buffer_id) != prev_buffer_id; - todo!() + if start <= cursor.start().0 && end > cursor.start().0 { + let excerpt = cursor.item()?; + let starts_new_buffer = Some(excerpt.buffer_id) != prev_buffer_id; + 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::>(), [Some(3)]); assert_eq!(snapshot.buffer_rows(5).collect::>(), []); - assert!(!snapshot.range_contains_excerpt_boundary(Point::new(1, 0)..Point::new(1, 5))); - assert!(snapshot.range_contains_excerpt_boundary(Point::new(1, 0)..Point::new(2, 0))); - assert!(snapshot.range_contains_excerpt_boundary(Point::new(1, 0)..Point::new(4, 0))); - assert!(!snapshot.range_contains_excerpt_boundary(Point::new(2, 0)..Point::new(3, 0))); - 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 + .excerpt_boundaries_in_range(Point::new(1, 0)..Point::new(1, 5)) + .next() + .is_none()); + assert!(snapshot + .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!( snapshot .excerpt_boundaries_in_range(Point::new(0, 0)..Point::new(4, 2)) + .map(|boundary| ( + boundary.row, + boundary + .buffer + .text_for_range(boundary.range) + .collect::(), + boundary.starts_new_buffer + )) .collect::>(), &[ - (Some(buffer_1.clone()), true), - (Some(buffer_1.clone()), false), - (Some(buffer_2.clone()), false), + (0, "".to_string(), true), + (0, "".to_string(), true), + (0, "".to_string(), true), + (0, "".to_string(), true), + (0, "".to_string(), true), + (0, "".to_string(), true), ] ); diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 7b903c67b0d73b3e555dc7755e6c2e95ca8dd5bd..0be632fecd6ea88516d0514c3e04c0b94dbb0fa6 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -68,6 +68,7 @@ pub struct Buffer { remote_selections: TreeMap, selections_update_count: usize, diagnostics_update_count: usize, + file_update_count: usize, language_server: Option, completion_triggers: Vec, deferred_ops: OperationQueue, @@ -78,8 +79,10 @@ pub struct Buffer { pub struct BufferSnapshot { text: text::BufferSnapshot, tree: Option, + path: Option>, diagnostics: DiagnosticSet, diagnostics_update_count: usize, + file_update_count: usize, remote_selections: TreeMap, selections_update_count: usize, is_parsing: bool, @@ -498,6 +501,7 @@ impl Buffer { selections_update_count: 0, diagnostics: Default::default(), diagnostics_update_count: 0, + file_update_count: 0, language_server: None, completion_triggers: Default::default(), deferred_ops: OperationQueue::new(), @@ -510,9 +514,11 @@ impl Buffer { BufferSnapshot { text: self.text.snapshot(), tree: self.syntax_tree(), + path: self.file.as_ref().map(|f| f.path().clone()), remote_selections: self.remote_selections.clone(), diagnostics: self.diagnostics.clone(), diagnostics_update_count: self.diagnostics_update_count, + file_update_count: self.file_update_count, is_parsing: self.parsing_in_background, language: self.language.clone(), parse_count: self.parse_count, @@ -722,6 +728,7 @@ impl Buffer { self.saved_version = version; if let Some(new_file) = new_file { self.file = Some(new_file); + self.file_update_count += 1; } if let Some((state, local_file)) = &self .language_server @@ -744,6 +751,7 @@ impl Buffer { .detach() } cx.emit(Event::Saved); + cx.notify(); } pub fn did_reload( @@ -819,7 +827,9 @@ impl Buffer { } if file_changed { + self.file_update_count += 1; cx.emit(Event::FileHandleChanged); + cx.notify(); } self.file = Some(new_file); task @@ -849,6 +859,10 @@ impl Buffer { self.diagnostics_update_count } + pub fn file_update_count(&self) -> usize { + self.file_update_count + } + pub(crate) fn syntax_tree(&self) -> Option { if let Some(syntax_tree) = self.syntax_tree.lock().as_mut() { self.interpolate_tree(syntax_tree); @@ -2231,6 +2245,14 @@ impl BufferSnapshot { pub fn selections_update_count(&self) -> usize { self.selections_update_count } + + pub fn path(&self) -> Option<&Arc> { + self.path.as_ref() + } + + pub fn file_update_count(&self) -> usize { + self.file_update_count + } } impl Clone for BufferSnapshot { @@ -2238,10 +2260,12 @@ impl Clone for BufferSnapshot { Self { text: self.text.clone(), tree: self.tree.clone(), + path: self.path.clone(), remote_selections: self.remote_selections.clone(), diagnostics: self.diagnostics.clone(), selections_update_count: self.selections_update_count, diagnostics_update_count: self.diagnostics_update_count, + file_update_count: self.file_update_count, is_parsing: self.is_parsing, language: self.language.clone(), parse_count: self.parse_count,