From 0c714210ffd70728635f54e19a16106ca6aedaf4 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 1 Dec 2021 15:25:55 -0800 Subject: [PATCH] Start work on generalizing the BlockMap to allow arbitrary elements Co-Authored-By: Nathan Sobo --- crates/editor/src/display_map.rs | 49 +- crates/editor/src/display_map/block_map.rs | 1100 ++++++++------------ crates/editor/src/display_map/wrap_map.rs | 22 +- crates/editor/src/editor.rs | 126 +-- crates/editor/src/element.rs | 165 ++- crates/gpui/src/elements.rs | 4 + crates/theme/src/theme.rs | 10 - 7 files changed, 579 insertions(+), 897 deletions(-) diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index 8137e46583..fa0f173318 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -3,13 +3,12 @@ mod fold_map; mod tab_map; mod wrap_map; -pub use block_map::{BlockDisposition, BlockId, BlockProperties, BufferRows, Chunks}; +pub use block_map::{ + AlignedBlock, BlockContext, BlockDisposition, BlockId, BlockProperties, BufferRows, Chunks, +}; use block_map::{BlockMap, BlockPoint}; use fold_map::{FoldMap, ToFoldPoint as _}; -use gpui::{ - fonts::{FontId, HighlightStyle}, - AppContext, Entity, ModelContext, ModelHandle, -}; +use gpui::{fonts::FontId, ElementBox, Entity, ModelContext, ModelHandle}; use language::{Anchor, Buffer, Point, Subscription as BufferSubscription, ToOffset, ToPoint}; use std::{ collections::{HashMap, HashSet}, @@ -17,8 +16,7 @@ use std::{ }; use sum_tree::Bias; use tab_map::TabMap; -use text::Rope; -use theme::{BlockStyle, SyntaxTheme}; +use theme::SyntaxTheme; use wrap_map::WrapMap; pub trait ToDisplayPoint { @@ -124,14 +122,13 @@ impl DisplayMap { self.block_map.read(snapshot, edits, cx); } - pub fn insert_blocks( + pub fn insert_blocks

( &mut self, - blocks: impl IntoIterator>, + blocks: impl IntoIterator>, cx: &mut ModelContext, ) -> Vec where P: ToOffset + Clone, - T: Into + Clone, { let snapshot = self.buffer.read(cx).snapshot(); let edits = self.buffer_subscription.consume().into_inner(); @@ -144,12 +141,11 @@ impl DisplayMap { block_map.insert(blocks, cx) } - pub fn restyle_blocks(&mut self, styles: HashMap, Option)>) + pub fn replace_blocks(&mut self, styles: HashMap) where - F1: 'static + Fn(&AppContext) -> Vec<(usize, HighlightStyle)>, - F2: 'static + Fn(&AppContext) -> BlockStyle, + F: 'static + Fn(&BlockContext) -> ElementBox, { - self.block_map.restyle(styles); + self.block_map.replace(styles); } pub fn remove_blocks(&mut self, ids: HashSet, cx: &mut ModelContext) { @@ -198,8 +194,8 @@ impl DisplayMapSnapshot { self.buffer_snapshot.len() == 0 } - pub fn buffer_rows<'a>(&'a self, start_row: u32, cx: Option<&'a AppContext>) -> BufferRows<'a> { - self.blocks_snapshot.buffer_rows(start_row, cx) + pub fn buffer_rows<'a>(&'a self, start_row: u32) -> BufferRows<'a> { + self.blocks_snapshot.buffer_rows(start_row) } pub fn buffer_row_count(&self) -> u32 { @@ -256,7 +252,7 @@ impl DisplayMapSnapshot { pub fn text_chunks(&self, display_row: u32) -> impl Iterator { self.blocks_snapshot - .chunks(display_row..self.max_point().row() + 1, None, None) + .chunks(display_row..self.max_point().row() + 1, None) .map(|h| h.text) } @@ -264,9 +260,8 @@ impl DisplayMapSnapshot { &'a self, display_rows: Range, theme: Option<&'a SyntaxTheme>, - cx: &'a AppContext, ) -> block_map::Chunks<'a> { - self.blocks_snapshot.chunks(display_rows, theme, Some(cx)) + self.blocks_snapshot.chunks(display_rows, theme) } pub fn chars_at<'a>(&'a self, point: DisplayPoint) -> impl Iterator + 'a { @@ -322,6 +317,13 @@ impl DisplayMapSnapshot { self.folds_snapshot.folds_in_range(range) } + pub fn blocks_in_range<'a>( + &'a self, + rows: Range, + ) -> impl Iterator { + self.blocks_snapshot.blocks_in_range(rows) + } + pub fn intersects_fold(&self, offset: T) -> bool { self.folds_snapshot.intersects_fold(offset) } @@ -448,13 +450,6 @@ impl ToDisplayPoint for Anchor { } } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum DisplayRow { - Buffer(u32), - Block(BlockId, Option), - Wrap, -} - #[cfg(test)] mod tests { use super::*; @@ -1065,7 +1060,7 @@ mod tests { ) -> Vec<(String, Option)> { let snapshot = map.update(cx, |map, cx| map.snapshot(cx)); let mut chunks: Vec<(String, Option)> = Vec::new(); - for chunk in snapshot.chunks(rows, Some(theme), cx) { + for chunk in snapshot.chunks(rows, Some(theme)) { let color = chunk.highlight_style.map(|s| s.color); if let Some((last_chunk, last_color)) = chunks.last_mut() { if color == *last_color { diff --git a/crates/editor/src/display_map/block_map.rs b/crates/editor/src/display_map/block_map.rs index 5ed1ac65fb..1cb5d961eb 100644 --- a/crates/editor/src/display_map/block_map.rs +++ b/crates/editor/src/display_map/block_map.rs @@ -1,26 +1,23 @@ -use super::{ - wrap_map::{self, Edit as WrapEdit, Snapshot as WrapSnapshot, WrapPoint}, - BlockStyle, DisplayRow, -}; -use gpui::{fonts::HighlightStyle, AppContext, ModelHandle}; +use super::wrap_map::{self, Edit as WrapEdit, Snapshot as WrapSnapshot, WrapPoint}; +use gpui::{AppContext, ElementBox, ModelHandle}; use language::{Buffer, Chunk}; use parking_lot::Mutex; use std::{ cmp::{self, Ordering}, collections::{HashMap, HashSet}, fmt::Debug, - iter, ops::{Deref, Range}, sync::{ atomic::{AtomicUsize, Ordering::SeqCst}, Arc, }, - vec, }; use sum_tree::SumTree; -use text::{rope, Anchor, Bias, Edit, Point, Rope, ToOffset, ToPoint as _}; +use text::{Anchor, Bias, Edit, Point, ToOffset, ToPoint as _}; use theme::SyntaxTheme; +const NEWLINES: &'static [u8] = &[b'\n'; u8::MAX as usize]; + pub struct BlockMap { buffer: ModelHandle, next_block_id: AtomicUsize, @@ -51,25 +48,28 @@ struct WrapRow(u32); pub struct Block { id: BlockId, position: Anchor, - text: Rope, - build_runs: Mutex Vec<(usize, HighlightStyle)>>>>, - build_style: Mutex BlockStyle>>>, + height: u8, + render: Mutex ElementBox>>, disposition: BlockDisposition, } #[derive(Clone)] -pub struct BlockProperties +pub struct BlockProperties

where P: Clone, - T: Clone, { pub position: P, - pub text: T, - pub build_runs: Option Vec<(usize, HighlightStyle)>>>, - pub build_style: Option BlockStyle>>, + pub height: u8, + pub render: Arc ElementBox>, pub disposition: BlockDisposition, } +pub struct BlockContext<'a> { + pub cx: &'a AppContext, + pub gutter_width: f32, + pub anchor_x: f32, +} + #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum BlockDisposition { Above, @@ -83,7 +83,7 @@ struct Transform { } #[derive(Clone, Debug)] -struct AlignedBlock { +pub struct AlignedBlock { block: Arc, column: u32, } @@ -92,35 +92,20 @@ struct AlignedBlock { struct TransformSummary { input_rows: u32, output_rows: u32, - longest_row_in_block: u32, - longest_row_in_block_chars: u32, } pub struct Chunks<'a> { transforms: sum_tree::Cursor<'a, Transform, (BlockRow, WrapRow)>, input_chunks: wrap_map::Chunks<'a>, input_chunk: Chunk<'a>, - block_chunks: Option>, output_row: u32, max_output_row: u32, - cx: Option<&'a AppContext>, -} - -struct BlockChunks<'a> { - chunks: rope::Chunks<'a>, - runs: iter::Peekable>, - chunk: Option<&'a str>, - remaining_padding: u32, - padding_column: u32, - run_start: usize, - offset: usize, } pub struct BufferRows<'a> { transforms: sum_tree::Cursor<'a, Transform, (BlockRow, WrapRow)>, input_buffer_rows: wrap_map::BufferRows<'a>, output_row: u32, - cx: Option<&'a AppContext>, started: bool, } @@ -333,19 +318,13 @@ impl BlockMap { *transforms = new_transforms; } - pub fn restyle(&mut self, mut styles: HashMap, Option)>) + pub fn replace(&mut self, mut element_builders: HashMap) where - F1: 'static + Fn(&AppContext) -> Vec<(usize, HighlightStyle)>, - F2: 'static + Fn(&AppContext) -> BlockStyle, + F: 'static + Fn(&BlockContext) -> ElementBox, { for block in &self.blocks { - if let Some((build_runs, build_style)) = styles.remove(&block.id) { - *block.build_runs.lock() = build_runs.map(|build_runs| { - Arc::new(build_runs) as Arc Vec<(usize, HighlightStyle)>> - }); - *block.build_style.lock() = build_style.map(|build_style| { - Arc::new(build_style) as Arc BlockStyle> - }); + if let Some(build_element) = element_builders.remove(&block.id) { + *block.render.lock() = Arc::new(build_element); } } } @@ -393,14 +372,13 @@ impl std::ops::DerefMut for BlockPoint { } impl<'a> BlockMapWriter<'a> { - pub fn insert( + pub fn insert

( &mut self, - blocks: impl IntoIterator>, + blocks: impl IntoIterator>, cx: &AppContext, ) -> Vec where P: ToOffset + Clone, - T: Into + Clone, { let buffer = self.0.buffer.read(cx); let mut ids = Vec::new(); @@ -436,9 +414,8 @@ impl<'a> BlockMapWriter<'a> { Arc::new(Block { id, position, - text: block.text.into(), - build_runs: Mutex::new(block.build_runs), - build_style: Mutex::new(block.build_style), + height: block.height, + render: Mutex::new(block.render), disposition: block.disposition, }), ); @@ -495,17 +472,12 @@ impl<'a> BlockMapWriter<'a> { impl BlockSnapshot { #[cfg(test)] fn text(&mut self) -> String { - self.chunks(0..self.transforms.summary().output_rows, None, None) + self.chunks(0..self.transforms.summary().output_rows, None) .map(|chunk| chunk.text) .collect() } - pub fn chunks<'a>( - &'a self, - rows: Range, - theme: Option<&'a SyntaxTheme>, - cx: Option<&'a AppContext>, - ) -> Chunks<'a> { + pub fn chunks<'a>(&'a self, rows: Range, theme: Option<&'a SyntaxTheme>) -> Chunks<'a> { let max_output_row = cmp::min(rows.end, self.transforms.summary().output_rows); let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(); let input_end = { @@ -535,15 +507,13 @@ impl BlockSnapshot { Chunks { input_chunks: self.wrap_snapshot.chunks(input_start..input_end, theme), input_chunk: Default::default(), - block_chunks: None, transforms: cursor, output_row: rows.start, max_output_row, - cx, } } - pub fn buffer_rows<'a>(&'a self, start_row: u32, cx: Option<&'a AppContext>) -> BufferRows<'a> { + pub fn buffer_rows<'a>(&'a self, start_row: u32) -> BufferRows<'a> { let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(); cursor.seek(&BlockRow(start_row), Bias::Right, &()); let (output_start, input_start) = cursor.start(); @@ -554,7 +524,6 @@ impl BlockSnapshot { }; let input_start_row = input_start.0 + overshoot; BufferRows { - cx, transforms: cursor, input_buffer_rows: self.wrap_snapshot.buffer_rows(input_start_row), output_row: start_row, @@ -562,6 +531,29 @@ impl BlockSnapshot { } } + pub fn blocks_in_range<'a>( + &'a self, + rows: Range, + ) -> impl Iterator { + let mut cursor = self.transforms.cursor::(); + cursor.seek(&BlockRow(rows.start), Bias::Right, &()); + std::iter::from_fn(move || { + while let Some(transform) = cursor.item() { + let start_row = cursor.start().0; + if start_row >= rows.end { + break; + } + if let Some(block) = &transform.block { + cursor.next(&()); + return Some((start_row, block)); + } else { + cursor.next(&()); + } + } + None + }) + } + pub fn max_point(&self) -> BlockPoint { let row = self.transforms.summary().output_rows - 1; BlockPoint::new(row, self.line_len(row)) @@ -569,18 +561,7 @@ impl BlockSnapshot { pub fn longest_row(&self) -> u32 { let input_row = self.wrap_snapshot.longest_row(); - let input_row_chars = self.wrap_snapshot.line_char_count(input_row); - let TransformSummary { - longest_row_in_block: block_row, - longest_row_in_block_chars: block_row_chars, - .. - } = &self.transforms.summary(); - - if *block_row_chars > input_row_chars { - *block_row - } else { - self.to_block_point(WrapPoint::new(input_row, 0)).row - } + self.to_block_point(WrapPoint::new(input_row, 0)).row } pub fn line_len(&self, row: u32) -> u32 { @@ -589,12 +570,8 @@ impl BlockSnapshot { if let Some(transform) = cursor.item() { let (output_start, input_start) = cursor.start(); let overshoot = row - output_start.0; - if let Some(block) = &transform.block { - let mut len = block.text.line_len(overshoot); - if len > 0 { - len += block.column; - } - len + if transform.block.is_some() { + 0 } else { self.wrap_snapshot.line_len(input_start.0 + overshoot) } @@ -697,21 +674,16 @@ impl Transform { summary: TransformSummary { input_rows: rows, output_rows: rows, - longest_row_in_block: 0, - longest_row_in_block_chars: 0, }, block: None, } } fn block(block: Arc, column: u32) -> Self { - let text_summary = block.text.summary(); Self { summary: TransformSummary { input_rows: 0, - output_rows: text_summary.lines.row + 1, - longest_row_in_block: text_summary.longest_row, - longest_row_in_block_chars: column + text_summary.longest_row_chars, + output_rows: block.height as u32, }, block: Some(AlignedBlock { block, column }), } @@ -730,37 +702,20 @@ impl<'a> Iterator for Chunks<'a> { return None; } - if let Some(block_chunks) = self.block_chunks.as_mut() { - if let Some(block_chunk) = block_chunks.next() { - self.output_row += block_chunk.text.matches('\n').count() as u32; - return Some(block_chunk); - } else { - self.block_chunks.take(); - self.output_row += 1; - if self.output_row < self.max_output_row { - return Some(Chunk { - text: "\n", - ..Default::default() - }); - } else { - return None; - } - } - } - let transform = self.transforms.item()?; - if let Some(block) = transform.block.as_ref() { + if transform.block.is_some() { let block_start = self.transforms.start().0 .0; let block_end = self.transforms.end(&()).0 .0; let start_in_block = self.output_row - block_start; let end_in_block = cmp::min(self.max_output_row, block_end) - block_start; + let line_count = end_in_block - start_in_block; + self.output_row += line_count; self.transforms.next(&()); - self.block_chunks = Some(BlockChunks::new( - block, - start_in_block..end_in_block, - self.cx, - )); - return self.next(); + return Some(Chunk { + text: unsafe { std::str::from_utf8_unchecked(&NEWLINES[..line_count as usize]) }, + highlight_style: None, + diagnostic: None, + }); } if self.input_chunk.text.is_empty() { @@ -797,107 +752,8 @@ impl<'a> Iterator for Chunks<'a> { } } -impl<'a> BlockChunks<'a> { - fn new(block: &'a AlignedBlock, rows: Range, cx: Option<&'a AppContext>) -> Self { - let offset_range = block.text.point_to_offset(Point::new(rows.start, 0)) - ..block.text.point_to_offset(Point::new(rows.end, 0)); - - let mut runs = block - .build_runs - .lock() - .as_ref() - .zip(cx) - .map(|(build_runs, cx)| build_runs(cx)) - .unwrap_or_default() - .into_iter() - .peekable(); - let mut run_start = 0; - while let Some((run_len, _)) = runs.peek() { - let run_end = run_start + run_len; - if run_end <= offset_range.start { - run_start = run_end; - runs.next(); - } else { - break; - } - } - - Self { - chunk: None, - run_start, - padding_column: block.column, - remaining_padding: block.column, - chunks: block.text.chunks_in_range(offset_range.clone()), - runs, - offset: offset_range.start, - } - } -} - -impl<'a> Iterator for BlockChunks<'a> { - type Item = Chunk<'a>; - - fn next(&mut self) -> Option { - if self.chunk.is_none() { - self.chunk = self.chunks.next(); - } - - let chunk = self.chunk?; - - if chunk.starts_with('\n') { - self.remaining_padding = 0; - } - - if self.remaining_padding > 0 { - const PADDING: &'static str = " "; - let padding_len = self.remaining_padding.min(PADDING.len() as u32); - self.remaining_padding -= padding_len; - return Some(Chunk { - text: &PADDING[..padding_len as usize], - ..Default::default() - }); - } - - let mut chunk_len = if let Some(ix) = chunk.find('\n') { - ix + 1 - } else { - chunk.len() - }; - - let mut highlight_style = None; - if let Some((run_len, style)) = self.runs.peek() { - highlight_style = Some(style.clone()); - let run_end_in_chunk = self.run_start + run_len - self.offset; - if run_end_in_chunk <= chunk_len { - chunk_len = run_end_in_chunk; - self.run_start += run_len; - self.runs.next(); - } - } - - self.offset += chunk_len; - let (chunk, suffix) = chunk.split_at(chunk_len); - - if chunk.ends_with('\n') { - self.remaining_padding = self.padding_column; - } - - self.chunk = if suffix.is_empty() { - None - } else { - Some(suffix) - }; - - Some(Chunk { - text: chunk, - highlight_style, - diagnostic: None, - }) - } -} - impl<'a> Iterator for BufferRows<'a> { - type Item = DisplayRow; + type Item = Option; fn next(&mut self) -> Option { if self.started { @@ -911,11 +767,8 @@ impl<'a> Iterator for BufferRows<'a> { } let transform = self.transforms.item()?; - if let Some(block) = &transform.block { - let style = self - .cx - .and_then(|cx| block.build_style.lock().as_ref().map(|f| f(cx))); - Some(DisplayRow::Block(block.id, style)) + if transform.block.is_some() { + Some(None) } else { Some(self.input_buffer_rows.next().unwrap()) } @@ -934,11 +787,6 @@ impl sum_tree::Summary for TransformSummary { type Context = (); fn add_summary(&mut self, summary: &Self, _: &()) { - if summary.longest_row_in_block_chars > self.longest_row_in_block_chars { - self.longest_row_in_block_chars = summary.longest_row_in_block_chars; - self.longest_row_in_block = self.output_rows + summary.longest_row_in_block; - } - self.input_rows += summary.input_rows; self.output_rows += summary.output_rows; } @@ -962,6 +810,20 @@ 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) + } +} + impl Deref for AlignedBlock { type Target = Block; @@ -975,7 +837,6 @@ impl Debug for Block { f.debug_struct("Block") .field("id", &self.id) .field("position", &self.position) - .field("text", &self.text) .field("disposition", &self.disposition) .finish() } @@ -1003,7 +864,7 @@ fn offset_for_row(s: &str, target: u32) -> (u32, usize) { mod tests { use super::*; use crate::display_map::{fold_map::FoldMap, tab_map::TabMap, wrap_map::WrapMap}; - use gpui::color::Color; + use gpui::{elements::Empty, Element}; use language::Buffer; use rand::prelude::*; use std::env; @@ -1023,76 +884,6 @@ mod tests { assert_eq!(offset_for_row("abc\ndef\nghi", 3), (2, 11)); } - #[gpui::test] - fn test_block_chunks(cx: &mut gpui::MutableAppContext) { - let red = Color::red(); - let blue = Color::blue(); - let clear = Color::default(); - - let block = AlignedBlock { - column: 5, - block: Arc::new(Block { - id: BlockId(0), - position: Anchor::min(), - text: "one!\ntwo three\nfour".into(), - build_style: Mutex::new(None), - build_runs: Mutex::new(Some(Arc::new(move |_| { - vec![(3, red.into()), (6, Default::default()), (5, blue.into())] - }))), - disposition: BlockDisposition::Above, - }), - }; - - assert_eq!( - colored_chunks(&block, 0..3, cx), - &[ - (" ", clear), - ("one", red), - ("!\n", clear), - (" ", clear), - ("two ", clear), - ("three", blue), - ("\n", clear), - (" ", clear), - ("four", clear) - ] - ); - assert_eq!( - colored_chunks(&block, 0..1, cx), - &[ - (" ", clear), // - ("one", red), - ("!\n", clear), - ] - ); - assert_eq!( - colored_chunks(&block, 1..3, cx), - &[ - (" ", clear), - ("two ", clear), - ("three", blue), - ("\n", clear), - (" ", clear), - ("four", clear) - ] - ); - - fn colored_chunks<'a>( - block: &'a AlignedBlock, - row_range: Range, - cx: &'a AppContext, - ) -> Vec<(&'a str, Color)> { - BlockChunks::new(block, row_range, Some(cx)) - .map(|c| { - ( - c.text, - c.highlight_style.map_or(Color::default(), |s| s.color), - ) - }) - .collect() - } - } - #[gpui::test] fn test_basic_blocks(cx: &mut gpui::MutableAppContext) { let family_id = cx.font_cache().load_family(&["Helvetica"]).unwrap(); @@ -1114,34 +905,56 @@ mod tests { vec![ BlockProperties { position: Point::new(1, 0), - text: "BLOCK 1", + height: 1, disposition: BlockDisposition::Above, - build_runs: None, - build_style: None, + render: Arc::new(|_| Empty::new().named("block 1")), }, BlockProperties { position: Point::new(1, 2), - text: "BLOCK 2", + height: 2, disposition: BlockDisposition::Above, - build_runs: None, - build_style: None, + render: Arc::new(|_| Empty::new().named("block 2")), }, BlockProperties { - position: Point::new(3, 2), - text: "BLOCK 3", + position: Point::new(3, 3), + height: 3, disposition: BlockDisposition::Below, - build_runs: None, - build_style: None, + render: Arc::new(|_| Empty::new().named("block 3")), }, ], cx, ); let mut snapshot = block_map.read(wraps_snapshot, vec![], cx); + assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n\n"); + + let blocks = snapshot + .blocks_in_range(0..8) + .map(|(start_row, block)| { + ( + start_row..start_row + block.height(), + block.column(), + block + .render(&BlockContext { + cx, + gutter_width: 0., + anchor_x: 0., + }) + .name() + .unwrap() + .to_string(), + ) + }) + .collect::>(); assert_eq!( - snapshot.text(), - "aaa\nBLOCK 1\n BLOCK 2\nbbb\nccc\nddd\n BLOCK 3" + blocks, + &[ + (1..2, 0, "block 1".to_string()), + (2..4, 2, "block 2".to_string()), + (7..10, 3, "block 3".to_string()), + ] ); + assert_eq!( snapshot.to_block_point(WrapPoint::new(0, 3)), BlockPoint::new(0, 3) @@ -1214,16 +1027,8 @@ mod tests { ); assert_eq!( - snapshot.buffer_rows(0, None).collect::>(), - &[ - DisplayRow::Buffer(0), - DisplayRow::Block(block_ids[0], None), - DisplayRow::Block(block_ids[1], None), - DisplayRow::Buffer(1), - DisplayRow::Buffer(2), - DisplayRow::Buffer(3), - DisplayRow::Block(block_ids[2], None) - ] + snapshot.buffer_rows(0).collect::>(), + &[Some(0), None, None, Some(1), Some(2), Some(3), None] ); // Insert a line break, separating two block decorations into separate @@ -1240,371 +1045,368 @@ mod tests { wrap_map.sync(tabs_snapshot, tab_edits, cx) }); let mut snapshot = block_map.read(wraps_snapshot, wrap_edits, cx); - assert_eq!( - snapshot.text(), - "aaa\nBLOCK 1\nb!!!\n BLOCK 2\nbb\nccc\nddd\n BLOCK 3" - ); + assert_eq!(snapshot.text(), "aaa\n\nb!!!\n\n\nbb\nccc\nddd\n\n\n"); } - #[gpui::test] - fn test_blocks_on_wrapped_lines(cx: &mut gpui::MutableAppContext) { - let family_id = cx.font_cache().load_family(&["Helvetica"]).unwrap(); - let font_id = cx - .font_cache() - .select_font(family_id, &Default::default()) - .unwrap(); + // #[gpui::test] + // fn test_blocks_on_wrapped_lines(cx: &mut gpui::MutableAppContext) { + // let family_id = cx.font_cache().load_family(&["Helvetica"]).unwrap(); + // let font_id = cx + // .font_cache() + // .select_font(family_id, &Default::default()) + // .unwrap(); - let text = "one two three\nfour five six\nseven eight"; + // let text = "one two three\nfour five six\nseven eight"; - let buffer = cx.add_model(|cx| Buffer::new(0, text, cx)); - let (_, folds_snapshot) = FoldMap::new(buffer.read(cx).snapshot()); - 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(buffer.clone(), wraps_snapshot.clone()); + // let buffer = cx.add_model(|cx| Buffer::new(0, text, cx)); + // let (_, folds_snapshot) = FoldMap::new(buffer.read(cx).snapshot()); + // 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(buffer.clone(), wraps_snapshot.clone()); - let mut writer = block_map.write(wraps_snapshot.clone(), vec![], cx); - writer.insert( - vec![ - BlockProperties { - position: Point::new(1, 12), - text: "BLOCK 2", - disposition: BlockDisposition::Below, - build_runs: None, - build_style: None, - }, - ], - cx, - ); + // let mut writer = block_map.write(wraps_snapshot.clone(), vec![], cx); + // writer.insert( + // vec![ + // BlockProperties { + // position: Point::new(1, 12), + // text: "BLOCK 2", + // disposition: BlockDisposition::Below, + // build_runs: None, + // build_style: None, + // }, + // ], + // cx, + // ); - // Blocks with an 'above' disposition go above their corresponding buffer line. - // Blocks with a 'below' disposition go below their corresponding buffer line. - let mut snapshot = block_map.read(wraps_snapshot, vec![], cx); - assert_eq!( - snapshot.text(), - "one two \nthree\n BLOCK 2\nseven \neight" - ); - } + // // Blocks with an 'above' disposition go above their corresponding buffer line. + // // Blocks with a 'below' disposition go below their corresponding buffer line. + // let mut snapshot = block_map.read(wraps_snapshot, vec![], cx); + // assert_eq!( + // snapshot.text(), + // "one two \nthree\n BLOCK 2\nseven \neight" + // ); + // } - #[gpui::test(iterations = 100)] - fn test_random_blocks(cx: &mut gpui::MutableAppContext, mut rng: StdRng) { - let operations = env::var("OPERATIONS") - .map(|i| i.parse().expect("invalid `OPERATIONS` variable")) - .unwrap_or(10); + // #[gpui::test(iterations = 100)] + // fn test_random_blocks(cx: &mut gpui::MutableAppContext, mut rng: StdRng) { + // let operations = env::var("OPERATIONS") + // .map(|i| i.parse().expect("invalid `OPERATIONS` variable")) + // .unwrap_or(10); - let wrap_width = if rng.gen_bool(0.2) { - None - } else { - Some(rng.gen_range(0.0..=100.0)) - }; - let tab_size = 1; - let family_id = cx.font_cache().load_family(&["Helvetica"]).unwrap(); - let font_id = cx - .font_cache() - .select_font(family_id, &Default::default()) - .unwrap(); - let font_size = 14.0; + // let wrap_width = if rng.gen_bool(0.2) { + // None + // } else { + // Some(rng.gen_range(0.0..=100.0)) + // }; + // let tab_size = 1; + // let family_id = cx.font_cache().load_family(&["Helvetica"]).unwrap(); + // let font_id = cx + // .font_cache() + // .select_font(family_id, &Default::default()) + // .unwrap(); + // let font_size = 14.0; - log::info!("Wrap width: {:?}", wrap_width); + // log::info!("Wrap width: {:?}", wrap_width); - let buffer = cx.add_model(|cx| { - let len = rng.gen_range(0..10); - let text = RandomCharIter::new(&mut rng).take(len).collect::(); - log::info!("initial buffer text: {:?}", text); - Buffer::new(0, text, cx) - }); - let mut buffer_snapshot = buffer.read(cx).snapshot(); - let (fold_map, folds_snapshot) = FoldMap::new(buffer_snapshot.clone()); - 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(buffer.clone(), wraps_snapshot); - let mut expected_blocks = Vec::new(); + // let buffer = cx.add_model(|cx| { + // let len = rng.gen_range(0..10); + // let text = RandomCharIter::new(&mut rng).take(len).collect::(); + // log::info!("initial buffer text: {:?}", text); + // Buffer::new(0, text, cx) + // }); + // let mut buffer_snapshot = buffer.read(cx).snapshot(); + // let (fold_map, folds_snapshot) = FoldMap::new(buffer_snapshot.clone()); + // 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(buffer.clone(), wraps_snapshot); + // let mut expected_blocks = Vec::new(); - for _ in 0..operations { - let mut buffer_edits = Vec::new(); - match rng.gen_range(0..=100) { - 0..=19 => { - let wrap_width = if rng.gen_bool(0.2) { - None - } else { - Some(rng.gen_range(0.0..=100.0)) - }; - log::info!("Setting wrap width to {:?}", wrap_width); - wrap_map.update(cx, |map, cx| map.set_wrap_width(wrap_width, cx)); - } - 20..=39 => { - let block_count = rng.gen_range(1..=1); - let block_properties = (0..block_count) - .map(|_| { - let buffer = buffer.read(cx); - let position = buffer.anchor_after( - buffer.clip_offset(rng.gen_range(0..=buffer.len()), Bias::Left), - ); + // for _ in 0..operations { + // let mut buffer_edits = Vec::new(); + // match rng.gen_range(0..=100) { + // 0..=19 => { + // let wrap_width = if rng.gen_bool(0.2) { + // None + // } else { + // Some(rng.gen_range(0.0..=100.0)) + // }; + // log::info!("Setting wrap width to {:?}", wrap_width); + // wrap_map.update(cx, |map, cx| map.set_wrap_width(wrap_width, cx)); + // } + // 20..=39 => { + // let block_count = rng.gen_range(1..=1); + // let block_properties = (0..block_count) + // .map(|_| { + // let buffer = buffer.read(cx); + // let position = buffer.anchor_after( + // buffer.clip_offset(rng.gen_range(0..=buffer.len()), Bias::Left), + // ); - let len = rng.gen_range(0..10); - let mut text = Rope::from( - RandomCharIter::new(&mut rng) - .take(len) - .collect::() - .to_uppercase() - .as_str(), - ); - let disposition = if rng.gen() { - text.push_front("<"); - BlockDisposition::Above - } else { - text.push_front(">"); - BlockDisposition::Below - }; - log::info!( - "inserting block {:?} {:?} with text {:?}", - disposition, - position.to_point(buffer), - text.to_string() - ); - BlockProperties { - position, - text, - disposition, - build_runs: None, - build_style: None, - } - }) - .collect::>(); + // let len = rng.gen_range(0..10); + // let mut text = Rope::from( + // RandomCharIter::new(&mut rng) + // .take(len) + // .collect::() + // .to_uppercase() + // .as_str(), + // ); + // let disposition = if rng.gen() { + // text.push_front("<"); + // BlockDisposition::Above + // } else { + // text.push_front(">"); + // BlockDisposition::Below + // }; + // log::info!( + // "inserting block {:?} {:?} with text {:?}", + // disposition, + // position.to_point(buffer), + // text.to_string() + // ); + // BlockProperties { + // position, + // text, + // disposition, + // build_runs: None, + // build_style: None, + // } + // }) + // .collect::>(); - let (folds_snapshot, fold_edits) = - fold_map.read(buffer_snapshot.clone(), vec![]); - let (tabs_snapshot, tab_edits) = tab_map.sync(folds_snapshot, fold_edits); - let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| { - wrap_map.sync(tabs_snapshot, tab_edits, cx) - }); - let mut block_map = block_map.write(wraps_snapshot, wrap_edits, cx); - let block_ids = block_map.insert(block_properties.clone(), cx); - for (block_id, props) in block_ids.into_iter().zip(block_properties) { - expected_blocks.push((block_id, props)); - } - } - 40..=59 if !expected_blocks.is_empty() => { - let block_count = rng.gen_range(1..=4.min(expected_blocks.len())); - let block_ids_to_remove = (0..block_count) - .map(|_| { - expected_blocks - .remove(rng.gen_range(0..expected_blocks.len())) - .0 - }) - .collect(); + // let (folds_snapshot, fold_edits) = + // fold_map.read(buffer_snapshot.clone(), vec![]); + // let (tabs_snapshot, tab_edits) = tab_map.sync(folds_snapshot, fold_edits); + // let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| { + // wrap_map.sync(tabs_snapshot, tab_edits, cx) + // }); + // let mut block_map = block_map.write(wraps_snapshot, wrap_edits, cx); + // let block_ids = block_map.insert(block_properties.clone(), cx); + // for (block_id, props) in block_ids.into_iter().zip(block_properties) { + // expected_blocks.push((block_id, props)); + // } + // } + // 40..=59 if !expected_blocks.is_empty() => { + // let block_count = rng.gen_range(1..=4.min(expected_blocks.len())); + // let block_ids_to_remove = (0..block_count) + // .map(|_| { + // expected_blocks + // .remove(rng.gen_range(0..expected_blocks.len())) + // .0 + // }) + // .collect(); - let (folds_snapshot, fold_edits) = - fold_map.read(buffer_snapshot.clone(), vec![]); - let (tabs_snapshot, tab_edits) = tab_map.sync(folds_snapshot, fold_edits); - let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| { - wrap_map.sync(tabs_snapshot, tab_edits, cx) - }); - let mut block_map = block_map.write(wraps_snapshot, wrap_edits, cx); - block_map.remove(block_ids_to_remove, cx); - } - _ => { - buffer.update(cx, |buffer, cx| { - let v0 = buffer.version(); - let edit_count = rng.gen_range(1..=5); - buffer.randomly_edit(&mut rng, edit_count, cx); - log::info!("buffer text: {:?}", buffer.text()); - buffer_edits.extend(buffer.edits_since(&v0)); - buffer_snapshot = buffer.snapshot(); - }); - } - } + // let (folds_snapshot, fold_edits) = + // fold_map.read(buffer_snapshot.clone(), vec![]); + // let (tabs_snapshot, tab_edits) = tab_map.sync(folds_snapshot, fold_edits); + // let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| { + // wrap_map.sync(tabs_snapshot, tab_edits, cx) + // }); + // let mut block_map = block_map.write(wraps_snapshot, wrap_edits, cx); + // block_map.remove(block_ids_to_remove, cx); + // } + // _ => { + // buffer.update(cx, |buffer, cx| { + // let v0 = buffer.version(); + // let edit_count = rng.gen_range(1..=5); + // buffer.randomly_edit(&mut rng, edit_count, cx); + // log::info!("buffer text: {:?}", buffer.text()); + // buffer_edits.extend(buffer.edits_since(&v0)); + // buffer_snapshot = buffer.snapshot(); + // }); + // } + // } - let (folds_snapshot, fold_edits) = fold_map.read(buffer_snapshot.clone(), buffer_edits); - let (tabs_snapshot, tab_edits) = tab_map.sync(folds_snapshot, fold_edits); - let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| { - wrap_map.sync(tabs_snapshot, tab_edits, cx) - }); - let mut blocks_snapshot = block_map.read(wraps_snapshot.clone(), wrap_edits, cx); - assert_eq!( - blocks_snapshot.transforms.summary().input_rows, - wraps_snapshot.max_point().row() + 1 - ); - log::info!("blocks text: {:?}", blocks_snapshot.text()); + // let (folds_snapshot, fold_edits) = fold_map.read(buffer_snapshot.clone(), buffer_edits); + // let (tabs_snapshot, tab_edits) = tab_map.sync(folds_snapshot, fold_edits); + // let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| { + // wrap_map.sync(tabs_snapshot, tab_edits, cx) + // }); + // let mut blocks_snapshot = block_map.read(wraps_snapshot.clone(), wrap_edits, cx); + // assert_eq!( + // blocks_snapshot.transforms.summary().input_rows, + // wraps_snapshot.max_point().row() + 1 + // ); + // log::info!("blocks text: {:?}", blocks_snapshot.text()); - let buffer = buffer.read(cx); - let mut sorted_blocks = expected_blocks - .iter() - .cloned() - .map(|(id, block)| { - let mut position = block.position.to_point(buffer); - let column = wraps_snapshot.from_point(position, Bias::Left).column(); - match block.disposition { - BlockDisposition::Above => { - position.column = 0; - } - BlockDisposition::Below => { - position.column = buffer.line_len(position.row); - } - }; - let row = wraps_snapshot.from_point(position, Bias::Left).row(); - ( - id, - BlockProperties { - position: BlockPoint::new(row, column), - text: block.text, - build_runs: block.build_runs.clone(), - build_style: None, - disposition: block.disposition, - }, - ) - }) - .collect::>(); - sorted_blocks - .sort_unstable_by_key(|(id, block)| (block.position.row, block.disposition, *id)); - let mut sorted_blocks = sorted_blocks.into_iter().peekable(); + // let buffer = buffer.read(cx); + // let mut sorted_blocks = expected_blocks + // .iter() + // .cloned() + // .map(|(id, block)| { + // let mut position = block.position.to_point(buffer); + // let column = wraps_snapshot.from_point(position, Bias::Left).column(); + // match block.disposition { + // BlockDisposition::Above => { + // position.column = 0; + // } + // BlockDisposition::Below => { + // position.column = buffer.line_len(position.row); + // } + // }; + // let row = wraps_snapshot.from_point(position, Bias::Left).row(); + // ( + // id, + // BlockProperties { + // position: BlockPoint::new(row, column), + // text: block.text, + // build_runs: block.build_runs.clone(), + // build_style: None, + // disposition: block.disposition, + // }, + // ) + // }) + // .collect::>(); + // sorted_blocks + // .sort_unstable_by_key(|(id, block)| (block.position.row, block.disposition, *id)); + // let mut sorted_blocks = sorted_blocks.into_iter().peekable(); - let mut expected_buffer_rows = Vec::new(); - let mut expected_text = String::new(); - let input_text = wraps_snapshot.text(); - for (row, input_line) in input_text.split('\n').enumerate() { - let row = row as u32; - if row > 0 { - expected_text.push('\n'); - } + // let mut expected_buffer_rows = Vec::new(); + // let mut expected_text = String::new(); + // let input_text = wraps_snapshot.text(); + // for (row, input_line) in input_text.split('\n').enumerate() { + // let row = row as u32; + // if row > 0 { + // expected_text.push('\n'); + // } - let buffer_row = wraps_snapshot - .to_point(WrapPoint::new(row, 0), Bias::Left) - .row; + // let buffer_row = wraps_snapshot + // .to_point(WrapPoint::new(row, 0), Bias::Left) + // .row; - while let Some((block_id, block)) = sorted_blocks.peek() { - if block.position.row == row && block.disposition == BlockDisposition::Above { - let text = block.text.to_string(); - let padding = " ".repeat(block.position.column as usize); - for line in text.split('\n') { - if !line.is_empty() { - expected_text.push_str(&padding); - expected_text.push_str(line); - } - expected_text.push('\n'); - expected_buffer_rows.push(DisplayRow::Block(*block_id, None)); - } - sorted_blocks.next(); - } else { - break; - } - } + // while let Some((block_id, block)) = sorted_blocks.peek() { + // if block.position.row == row && block.disposition == BlockDisposition::Above { + // let text = block.text.to_string(); + // let padding = " ".repeat(block.position.column as usize); + // for line in text.split('\n') { + // if !line.is_empty() { + // expected_text.push_str(&padding); + // expected_text.push_str(line); + // } + // expected_text.push('\n'); + // expected_buffer_rows.push(DisplayRow::Block(*block_id, None)); + // } + // sorted_blocks.next(); + // } else { + // break; + // } + // } - let soft_wrapped = wraps_snapshot.to_tab_point(WrapPoint::new(row, 0)).column() > 0; - expected_buffer_rows.push(if soft_wrapped { - DisplayRow::Wrap - } else { - DisplayRow::Buffer(buffer_row) - }); - expected_text.push_str(input_line); + // let soft_wrapped = wraps_snapshot.to_tab_point(WrapPoint::new(row, 0)).column() > 0; + // expected_buffer_rows.push(if soft_wrapped { + // DisplayRow::Wrap + // } else { + // DisplayRow::Buffer(buffer_row) + // }); + // expected_text.push_str(input_line); - while let Some((block_id, block)) = sorted_blocks.peek() { - if block.position.row == row && block.disposition == BlockDisposition::Below { - let text = block.text.to_string(); - let padding = " ".repeat(block.position.column as usize); - for line in text.split('\n') { - expected_text.push('\n'); - if !line.is_empty() { - expected_text.push_str(&padding); - expected_text.push_str(line); - } - expected_buffer_rows.push(DisplayRow::Block(*block_id, None)); - } - sorted_blocks.next(); - } else { - break; - } - } - } + // while let Some((block_id, block)) = sorted_blocks.peek() { + // if block.position.row == row && block.disposition == BlockDisposition::Below { + // let text = block.text.to_string(); + // let padding = " ".repeat(block.position.column as usize); + // for line in text.split('\n') { + // expected_text.push('\n'); + // if !line.is_empty() { + // expected_text.push_str(&padding); + // expected_text.push_str(line); + // } + // expected_buffer_rows.push(DisplayRow::Block(*block_id, None)); + // } + // sorted_blocks.next(); + // } else { + // break; + // } + // } + // } - let expected_lines = expected_text.split('\n').collect::>(); - let expected_row_count = expected_lines.len(); - for start_row in 0..expected_row_count { - let expected_text = expected_lines[start_row..].join("\n"); - let actual_text = blocks_snapshot - .chunks(start_row as u32..expected_row_count as u32, None, None) - .map(|chunk| chunk.text) - .collect::(); - assert_eq!( - actual_text, expected_text, - "incorrect text starting from row {}", - start_row - ); - assert_eq!( - blocks_snapshot - .buffer_rows(start_row as u32, None) - .collect::>(), - &expected_buffer_rows[start_row..] - ); - } + // let expected_lines = expected_text.split('\n').collect::>(); + // let expected_row_count = expected_lines.len(); + // for start_row in 0..expected_row_count { + // let expected_text = expected_lines[start_row..].join("\n"); + // let actual_text = blocks_snapshot + // .chunks(start_row as u32..expected_row_count as u32, None, None) + // .map(|chunk| chunk.text) + // .collect::(); + // assert_eq!( + // actual_text, expected_text, + // "incorrect text starting from row {}", + // start_row + // ); + // assert_eq!( + // blocks_snapshot + // .buffer_rows(start_row as u32, None) + // .collect::>(), + // &expected_buffer_rows[start_row..] + // ); + // } - let mut expected_longest_rows = Vec::new(); - let mut longest_line_len = -1_isize; - for (row, line) in expected_lines.iter().enumerate() { - let row = row as u32; + // let mut expected_longest_rows = Vec::new(); + // let mut longest_line_len = -1_isize; + // for (row, line) in expected_lines.iter().enumerate() { + // let row = row as u32; - assert_eq!( - blocks_snapshot.line_len(row), - line.len() as u32, - "invalid line len for row {}", - row - ); + // assert_eq!( + // blocks_snapshot.line_len(row), + // line.len() as u32, + // "invalid line len for row {}", + // row + // ); - let line_char_count = line.chars().count() as isize; - match line_char_count.cmp(&longest_line_len) { - Ordering::Less => {} - Ordering::Equal => expected_longest_rows.push(row), - Ordering::Greater => { - longest_line_len = line_char_count; - expected_longest_rows.clear(); - expected_longest_rows.push(row); - } - } - } + // let line_char_count = line.chars().count() as isize; + // match line_char_count.cmp(&longest_line_len) { + // Ordering::Less => {} + // Ordering::Equal => expected_longest_rows.push(row), + // Ordering::Greater => { + // longest_line_len = line_char_count; + // expected_longest_rows.clear(); + // expected_longest_rows.push(row); + // } + // } + // } - log::info!("getting longest row >>>>>>>>>>>>>>>>>>>>>>>>"); - let longest_row = blocks_snapshot.longest_row(); - assert!( - expected_longest_rows.contains(&longest_row), - "incorrect longest row {}. expected {:?} with length {}", - longest_row, - expected_longest_rows, - longest_line_len, - ); + // log::info!("getting longest row >>>>>>>>>>>>>>>>>>>>>>>>"); + // let longest_row = blocks_snapshot.longest_row(); + // assert!( + // expected_longest_rows.contains(&longest_row), + // "incorrect longest row {}. expected {:?} with length {}", + // longest_row, + // expected_longest_rows, + // longest_line_len, + // ); - for row in 0..=blocks_snapshot.wrap_snapshot.max_point().row() { - let wrap_point = WrapPoint::new(row, 0); - let block_point = blocks_snapshot.to_block_point(wrap_point); - assert_eq!(blocks_snapshot.to_wrap_point(block_point), wrap_point); - } + // for row in 0..=blocks_snapshot.wrap_snapshot.max_point().row() { + // let wrap_point = WrapPoint::new(row, 0); + // let block_point = blocks_snapshot.to_block_point(wrap_point); + // assert_eq!(blocks_snapshot.to_wrap_point(block_point), wrap_point); + // } - let mut block_point = BlockPoint::new(0, 0); - for c in expected_text.chars() { - let left_point = blocks_snapshot.clip_point(block_point, Bias::Left); - let right_point = blocks_snapshot.clip_point(block_point, Bias::Right); + // let mut block_point = BlockPoint::new(0, 0); + // for c in expected_text.chars() { + // let left_point = blocks_snapshot.clip_point(block_point, Bias::Left); + // let right_point = blocks_snapshot.clip_point(block_point, Bias::Right); - assert_eq!( - blocks_snapshot.to_block_point(blocks_snapshot.to_wrap_point(left_point)), - left_point - ); - assert_eq!( - blocks_snapshot.to_block_point(blocks_snapshot.to_wrap_point(right_point)), - right_point - ); + // assert_eq!( + // blocks_snapshot.to_block_point(blocks_snapshot.to_wrap_point(left_point)), + // left_point + // ); + // assert_eq!( + // blocks_snapshot.to_block_point(blocks_snapshot.to_wrap_point(right_point)), + // right_point + // ); - if c == '\n' { - block_point.0 += Point::new(1, 0); - } else { - block_point.column += c.len_utf8() as u32; - } - } - } - } + // if c == '\n' { + // block_point.0 += Point::new(1, 0); + // } else { + // block_point.column += c.len_utf8() as u32; + // } + // } + // } + // } } diff --git a/crates/editor/src/display_map/wrap_map.rs b/crates/editor/src/display_map/wrap_map.rs index 174e9df98e..04b6c00d6b 100644 --- a/crates/editor/src/display_map/wrap_map.rs +++ b/crates/editor/src/display_map/wrap_map.rs @@ -1,7 +1,6 @@ use super::{ fold_map, tab_map::{self, Edit as TabEdit, Snapshot as TabSnapshot, TabPoint}, - DisplayRow, }; use gpui::{ fonts::FontId, text_layout::LineWrapper, Entity, ModelContext, ModelHandle, MutableAppContext, @@ -607,13 +606,6 @@ impl Snapshot { len as u32 } - pub fn line_char_count(&self, row: u32) -> u32 { - self.text_chunks(row) - .flat_map(|c| c.chars()) - .take_while(|c| *c != '\n') - .count() as u32 - } - pub fn soft_wrap_indent(&self, row: u32) -> Option { let mut cursor = self.transforms.cursor::(); cursor.seek(&WrapPoint::new(row + 1, 0), Bias::Right, &()); @@ -719,11 +711,7 @@ impl Snapshot { prev_tab_row = tab_point.row(); soft_wrapped = false; } - expected_buffer_rows.push(if soft_wrapped { - DisplayRow::Wrap - } else { - DisplayRow::Buffer(buffer_row) - }); + expected_buffer_rows.push(if soft_wrapped { None } else { Some(buffer_row) }); } for start_display_row in 0..expected_buffer_rows.len() { @@ -803,7 +791,7 @@ impl<'a> Iterator for Chunks<'a> { } impl<'a> Iterator for BufferRows<'a> { - type Item = DisplayRow; + type Item = Option; fn next(&mut self) -> Option { if self.output_row > self.max_output_row { @@ -823,11 +811,7 @@ impl<'a> Iterator for BufferRows<'a> { self.soft_wrapped = true; } - Some(if soft_wrapped { - DisplayRow::Wrap - } else { - DisplayRow::Buffer(buffer_row) - }) + Some(if soft_wrapped { None } else { Some(buffer_row) }) } } diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index e61f1f79cd..c419b1dab5 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -8,11 +8,12 @@ mod test; use aho_corasick::AhoCorasick; use clock::ReplicaId; +pub use display_map::DisplayPoint; use display_map::*; -pub use display_map::{DisplayPoint, DisplayRow}; pub use element::*; use gpui::{ action, + elements::Text, geometry::vector::{vec2f, Vector2F}, keymap::Binding, text_layout, AppContext, ClipboardItem, Element, ElementBox, Entity, ModelHandle, @@ -28,14 +29,14 @@ use std::{ cmp, collections::HashMap, iter, mem, - ops::{Range, RangeInclusive}, + ops::{Deref, Range, RangeInclusive}, rc::Rc, sync::Arc, time::Duration, }; use sum_tree::Bias; use text::rope::TextDimension; -use theme::{DiagnosticStyle, EditorStyle, SyntaxTheme}; +use theme::{DiagnosticStyle, EditorStyle}; use util::post_inc; use workspace::{EntryOpener, Workspace}; @@ -2877,35 +2878,16 @@ impl Editor { active_diagnostics.is_valid = is_valid; let mut new_styles = HashMap::new(); for (block_id, diagnostic) in &active_diagnostics.blocks { - let severity = diagnostic.severity; - let message_len = diagnostic.message.len(); - new_styles.insert( - *block_id, - ( - Some({ - let build_settings = self.build_settings.clone(); - move |cx: &AppContext| { - let settings = build_settings.borrow()(cx); - vec![( - message_len, - diagnostic_style(severity, is_valid, &settings.style) - .text - .into(), - )] - } - }), - Some({ - let build_settings = self.build_settings.clone(); - move |cx: &AppContext| { - let settings = build_settings.borrow()(cx); - diagnostic_style(severity, is_valid, &settings.style).block - } - }), - ), - ); + let build_settings = self.build_settings.clone(); + let diagnostic = diagnostic.clone(); + new_styles.insert(*block_id, move |cx: &BlockContext| { + let diagnostic = diagnostic.clone(); + let settings = build_settings.borrow()(cx.cx); + render_diagnostic(diagnostic, &settings.style) + }); } self.display_map - .update(cx, |display_map, _| display_map.restyle_blocks(new_styles)); + .update(cx, |display_map, _| display_map.replace_blocks(new_styles)); } } } @@ -2940,30 +2922,17 @@ impl Editor { .insert_blocks( diagnostic_group.iter().map(|(range, diagnostic)| { let build_settings = self.build_settings.clone(); - let message_len = diagnostic.message.len(); - let severity = diagnostic.severity; + let diagnostic = diagnostic.clone(); + let message_height = diagnostic.message.lines().count() as u8; + BlockProperties { position: range.start, - text: diagnostic.message.as_str(), - build_runs: Some(Arc::new({ - let build_settings = build_settings.clone(); - move |cx| { - let settings = build_settings.borrow()(cx); - vec![( - message_len, - diagnostic_style(severity, true, &settings.style) - .text - .into(), - )] - } - })), - build_style: Some(Arc::new({ - let build_settings = build_settings.clone(); - move |cx| { - let settings = build_settings.borrow()(cx); - diagnostic_style(severity, true, &settings.style).block - } - })), + height: message_height, + render: Arc::new(move |cx| { + let settings = build_settings.borrow()(cx.cx); + let diagnostic = diagnostic.clone(); + render_diagnostic(diagnostic, &settings.style) + }), disposition: BlockDisposition::Below, } }), @@ -3482,10 +3451,6 @@ impl Editor { } impl Snapshot { - pub fn is_empty(&self) -> bool { - self.display_snapshot.is_empty() - } - pub fn is_focused(&self) -> bool { self.is_focused } @@ -3494,23 +3459,6 @@ impl Snapshot { self.placeholder_text.as_ref() } - pub fn buffer_row_count(&self) -> u32 { - self.display_snapshot.buffer_row_count() - } - - pub fn buffer_rows<'a>(&'a self, start_row: u32, cx: &'a AppContext) -> BufferRows<'a> { - self.display_snapshot.buffer_rows(start_row, Some(cx)) - } - - pub fn chunks<'a>( - &'a self, - display_rows: Range, - theme: Option<&'a SyntaxTheme>, - cx: &'a AppContext, - ) -> display_map::Chunks<'a> { - self.display_snapshot.chunks(display_rows, theme, cx) - } - pub fn scroll_position(&self) -> Vector2F { compute_scroll_position( &self.display_snapshot, @@ -3518,29 +3466,13 @@ impl Snapshot { &self.scroll_top_anchor, ) } +} - pub fn max_point(&self) -> DisplayPoint { - self.display_snapshot.max_point() - } +impl Deref for Snapshot { + type Target = DisplayMapSnapshot; - pub fn longest_row(&self) -> u32 { - self.display_snapshot.longest_row() - } - - pub fn line_len(&self, display_row: u32) -> u32 { - self.display_snapshot.line_len(display_row) - } - - pub fn line(&self, display_row: u32) -> String { - self.display_snapshot.line(display_row) - } - - pub fn prev_row_boundary(&self, point: DisplayPoint) -> (DisplayPoint, Point) { - self.display_snapshot.prev_row_boundary(point) - } - - pub fn next_row_boundary(&self, point: DisplayPoint) -> (DisplayPoint, Point) { - self.display_snapshot.next_row_boundary(point) + fn deref(&self) -> &Self::Target { + &self.display_snapshot } } @@ -3709,6 +3641,12 @@ impl SelectionExt for Selection { } } +fn render_diagnostic(diagnostic: Diagnostic, style: &EditorStyle) -> ElementBox { + let mut text_style = style.text.clone(); + text_style.color = diagnostic_style(diagnostic.severity, true, &style).text; + Text::new(diagnostic.message, text_style).boxed() +} + pub fn diagnostic_style( severity: DiagnosticSeverity, valid: bool, diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 5acc526014..13dd5cfdc7 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -1,6 +1,8 @@ +use crate::display_map::BlockContext; + use super::{ - DisplayPoint, DisplayRow, Editor, EditorMode, EditorSettings, EditorStyle, Input, Scroll, - Select, SelectPhase, Snapshot, SoftWrap, MAX_LINE_LEN, + DisplayPoint, Editor, EditorMode, EditorSettings, EditorStyle, Input, Scroll, Select, + SelectPhase, Snapshot, SoftWrap, MAX_LINE_LEN, }; use clock::ReplicaId; use gpui::{ @@ -13,7 +15,7 @@ use gpui::{ json::{self, ToJson}, keymap::Keystroke, text_layout::{self, RunStyle, TextLayoutCache}, - AppContext, Axis, Border, Element, Event, EventContext, FontCache, LayoutContext, + AppContext, Axis, Border, Element, ElementBox, Event, EventContext, FontCache, LayoutContext, MutableAppContext, PaintContext, Quad, Scene, SizeConstraint, ViewContext, WeakViewHandle, }; use json::json; @@ -25,7 +27,6 @@ use std::{ fmt::Write, ops::Range, }; -use theme::BlockStyle; pub struct EditorElement { view: WeakViewHandle, @@ -278,51 +279,6 @@ impl EditorElement { }); } } - - // Draw block backgrounds - for (ixs, block_style) in &layout.block_layouts { - let row = start_row + ixs.start; - let offset = vec2f(0., row as f32 * layout.line_height - scroll_top); - let height = ixs.len() as f32 * layout.line_height; - cx.scene.push_quad(Quad { - bounds: RectF::new( - text_bounds.origin() + offset, - vec2f(text_bounds.width(), height), - ), - background: block_style.background, - border: block_style - .border - .map_or(Default::default(), |color| Border { - width: 1., - color, - overlay: true, - top: true, - right: false, - bottom: true, - left: false, - }), - corner_radius: 0., - }); - cx.scene.push_quad(Quad { - bounds: RectF::new( - gutter_bounds.origin() + offset, - vec2f(gutter_bounds.width(), height), - ), - background: block_style.gutter_background, - border: block_style - .gutter_border - .map_or(Default::default(), |color| Border { - width: 1., - color, - overlay: true, - top: true, - right: false, - bottom: true, - left: false, - }), - corner_radius: 0., - }); - } } fn paint_gutter( @@ -461,6 +417,18 @@ impl EditorElement { cx.scene.pop_layer(); } + fn paint_blocks( + &mut self, + bounds: RectF, + visible_bounds: RectF, + layout: &LayoutState, + cx: &mut PaintContext, + ) { + for (row_range, block) in &layout.blocks { + // + } + } + fn max_line_number_width(&self, snapshot: &Snapshot, cx: &LayoutContext) -> f32 { let digit_count = (snapshot.buffer_row_count() as f32).log10().floor() as usize + 1; let style = &self.settings.style; @@ -487,18 +455,13 @@ impl EditorElement { active_rows: &BTreeMap, snapshot: &Snapshot, cx: &LayoutContext, - ) -> ( - Vec>, - Vec<(Range, BlockStyle)>, - ) { + ) -> Vec> { let style = &self.settings.style; let include_line_numbers = snapshot.mode == EditorMode::Full; - let mut last_block_id = None; - let mut blocks = Vec::<(Range, BlockStyle)>::new(); let mut line_number_layouts = Vec::with_capacity(rows.len()); let mut line_number = String::new(); for (ix, row) in snapshot - .buffer_rows(rows.start, cx) + .buffer_rows(rows.start) .take((rows.end - rows.start) as usize) .enumerate() { @@ -508,46 +471,29 @@ impl EditorElement { } else { style.line_number }; - match row { - DisplayRow::Buffer(buffer_row) => { - if include_line_numbers { - line_number.clear(); - write!(&mut line_number, "{}", buffer_row + 1).unwrap(); - line_number_layouts.push(Some(cx.text_layout_cache.layout_str( - &line_number, - style.text.font_size, - &[( - line_number.len(), - RunStyle { - font_id: style.text.font_id, - color, - underline: None, - }, - )], - ))); - } - last_block_id = None; - } - DisplayRow::Block(block_id, style) => { - let ix = ix as u32; - if last_block_id == Some(block_id) { - if let Some((row_range, _)) = blocks.last_mut() { - row_range.end += 1; - } - } else if let Some(style) = style { - blocks.push((ix..ix + 1, style)); - } - line_number_layouts.push(None); - last_block_id = Some(block_id); - } - DisplayRow::Wrap => { - line_number_layouts.push(None); - last_block_id = None; + if let Some(buffer_row) = row { + if include_line_numbers { + line_number.clear(); + write!(&mut line_number, "{}", buffer_row + 1).unwrap(); + line_number_layouts.push(Some(cx.text_layout_cache.layout_str( + &line_number, + style.text.font_size, + &[( + line_number.len(), + RunStyle { + font_id: style.text.font_id, + color, + underline: None, + }, + )], + ))); } + } else { + line_number_layouts.push(None); } } - (line_number_layouts, blocks) + line_number_layouts } fn layout_lines( @@ -598,7 +544,7 @@ impl EditorElement { let mut styles = Vec::new(); let mut row = rows.start; let mut line_exceeded_max_len = false; - let chunks = snapshot.chunks(rows.clone(), Some(&style.syntax), cx); + let chunks = snapshot.chunks(rows.clone(), Some(&style.syntax)); let newline_chunk = Chunk { text: "\n", @@ -668,6 +614,27 @@ impl EditorElement { layouts } + + fn layout_blocks( + &mut self, + rows: Range, + snapshot: &Snapshot, + cx: &LayoutContext, + ) -> Vec<(Range, ElementBox)> { + snapshot + .blocks_in_range(rows) + .map(|(start_row, block)| { + ( + start_row..start_row + block.height(), + block.render(&BlockContext { + cx, + gutter_width: 0.0, + anchor_x: 0.0, + }), + ) + }) + .collect() + } } impl Element for EditorElement { @@ -773,8 +740,7 @@ impl Element for EditorElement { } }); - let (line_number_layouts, block_layouts) = - self.layout_rows(start_row..end_row, &active_rows, &snapshot, cx); + let line_number_layouts = self.layout_rows(start_row..end_row, &active_rows, &snapshot, cx); let mut max_visible_line_width = 0.0; let line_layouts = self.layout_lines(start_row..end_row, &mut snapshot, cx); @@ -784,6 +750,8 @@ impl Element for EditorElement { } } + let blocks = self.layout_blocks(start_row..end_row, &snapshot, cx); + let mut layout = LayoutState { size, gutter_size, @@ -797,7 +765,7 @@ impl Element for EditorElement { highlighted_row, line_layouts, line_number_layouts, - block_layouts, + blocks, line_height, em_width, em_advance, @@ -853,6 +821,7 @@ impl Element for EditorElement { self.paint_gutter(gutter_bounds, visible_bounds, layout, cx); } self.paint_text(text_bounds, visible_bounds, layout, cx); + self.paint_blocks(text_bounds, visible_bounds, layout, cx); cx.scene.pop_layer(); @@ -927,7 +896,7 @@ pub struct LayoutState { highlighted_row: Option, line_layouts: Vec, line_number_layouts: Vec>, - block_layouts: Vec<(Range, BlockStyle)>, + blocks: Vec<(Range, ElementBox)>, line_height: f32, em_width: f32, em_advance: f32, @@ -1185,7 +1154,7 @@ mod tests { }); let element = EditorElement::new(editor.downgrade(), settings); - let (layouts, _) = editor.update(cx, |editor, cx| { + let layouts = editor.update(cx, |editor, cx| { let snapshot = editor.snapshot(cx); let mut presenter = cx.build_presenter(window_id, 30.); let mut layout_cx = presenter.build_layout_context(false, cx); diff --git a/crates/gpui/src/elements.rs b/crates/gpui/src/elements.rs index c8048ef3fa..c0b6cdd143 100644 --- a/crates/gpui/src/elements.rs +++ b/crates/gpui/src/elements.rs @@ -301,6 +301,10 @@ impl Default for Lifecycle { } impl ElementBox { + pub fn name(&self) -> Option<&str> { + self.0.name.as_deref() + } + pub fn metadata(&self) -> Option<&T> { let element = unsafe { &*self.0.element.as_ptr() }; element.metadata().and_then(|m| m.downcast_ref()) diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 716ce4a46a..6f685ce70d 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -253,8 +253,6 @@ pub struct EditorStyle { #[derive(Copy, Clone, Deserialize, Default)] pub struct DiagnosticStyle { pub text: Color, - #[serde(flatten)] - pub block: BlockStyle, } #[derive(Clone, Copy, Default, Deserialize)] @@ -273,14 +271,6 @@ pub struct InputEditorStyle { pub selection: SelectionStyle, } -#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Eq)] -pub struct BlockStyle { - pub background: Option, - pub border: Option, - pub gutter_background: Option, - pub gutter_border: Option, -} - impl EditorStyle { pub fn placeholder_text(&self) -> &TextStyle { self.placeholder_text.as_ref().unwrap_or(&self.text)