From e605a5ead298e2f186d07ecce864c6ed9d82f3cf Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 12 Nov 2021 14:51:25 -0700 Subject: [PATCH] Sketch an initial implementation for block_map::HighlightedChunks Co-Authored-By: Max Brunsfeld --- crates/buffer/src/rope.rs | 16 +++- crates/editor/src/display_map/block_map.rs | 104 +++++++++++++++++---- 2 files changed, 97 insertions(+), 23 deletions(-) diff --git a/crates/buffer/src/rope.rs b/crates/buffer/src/rope.rs index d5bba2ef2e172a7c057c26ea4dc7c4d9ebc877bd..24186c8056bba714e477f8935b92db6ba0636fa5 100644 --- a/crates/buffer/src/rope.rs +++ b/crates/buffer/src/rope.rs @@ -149,7 +149,9 @@ impl Rope { } pub fn offset_to_point(&self, offset: usize) -> Point { - assert!(offset <= self.summary().bytes); + if offset >= self.summary().bytes { + return self.summary().lines; + } let mut cursor = self.chunks.cursor::<(usize, Point)>(); cursor.seek(&offset, Bias::Left, &()); let overshoot = offset - cursor.start().0; @@ -160,7 +162,9 @@ impl Rope { } pub fn offset_to_point_utf16(&self, offset: usize) -> PointUtf16 { - assert!(offset <= self.summary().bytes); + if offset >= self.summary().bytes { + return self.summary().lines_utf16; + } let mut cursor = self.chunks.cursor::<(usize, PointUtf16)>(); cursor.seek(&offset, Bias::Left, &()); let overshoot = offset - cursor.start().0; @@ -171,7 +175,9 @@ impl Rope { } pub fn point_to_offset(&self, point: Point) -> usize { - assert!(point <= self.summary().lines); + if point >= self.summary().lines { + return self.summary().bytes; + } let mut cursor = self.chunks.cursor::<(Point, usize)>(); cursor.seek(&point, Bias::Left, &()); let overshoot = point - cursor.start().0; @@ -182,7 +188,9 @@ impl Rope { } pub fn point_utf16_to_offset(&self, point: PointUtf16) -> usize { - assert!(point <= self.summary().lines_utf16); + if point >= self.summary().lines_utf16 { + return self.summary().bytes; + } let mut cursor = self.chunks.cursor::<(PointUtf16, usize)>(); cursor.seek(&point, Bias::Left, &()); let overshoot = point - cursor.start().0; diff --git a/crates/editor/src/display_map/block_map.rs b/crates/editor/src/display_map/block_map.rs index b07bd2258a0cd640cdc024c4362174db1f318c5c..4496b1ad22535babc1704f14d30d2a026109de1c 100644 --- a/crates/editor/src/display_map/block_map.rs +++ b/crates/editor/src/display_map/block_map.rs @@ -3,6 +3,7 @@ use buffer::{rope, Anchor, Bias, Point, Rope, ToOffset}; use gpui::fonts::HighlightStyle; use language::HighlightedChunk; use parking_lot::Mutex; +use smol::io::AsyncBufReadExt; use std::{borrow::Borrow, cmp, collections::HashSet, iter, ops::Range, slice, sync::Arc}; use sum_tree::SumTree; @@ -63,11 +64,11 @@ struct TransformSummary { } struct HighlightedChunks<'a> { - transform_cursor: sum_tree::Cursor<'a, Transform, (OutputRow, InputRow)>, + transforms: sum_tree::Cursor<'a, Transform, (OutputRow, InputRow)>, input_chunks: wrap_map::HighlightedChunks<'a>, - input_chunk: Option>, + input_chunk: HighlightedChunk<'a>, block_chunks: Option>, - output_position: BlockPoint, + output_row: u32, max_output_row: u32, } @@ -163,6 +164,24 @@ impl BlockMap { } } +impl BlockPoint { + fn row(&self) -> u32 { + self.0.row + } + + fn row_mut(&self) -> &mut u32 { + &mut self.0.row + } + + fn column(&self) -> u32 { + self.0.column + } + + fn column_mut(&self) -> &mut u32 { + &mut self.0.column + } +} + impl<'a> BlockMapWriter<'a> { pub fn insert( &self, @@ -202,7 +221,7 @@ impl BlockSnapshot { input_chunks, input_chunk: None, block_chunks: None, - transform_cursor: cursor, + transforms: cursor, output_position: BlockPoint(Point::new(rows.start, 0)), max_output_row: rows.end, } @@ -258,35 +277,62 @@ impl<'a> Iterator for HighlightedChunks<'a> { type Item = HighlightedChunk<'a>; fn next(&mut self) -> Option { - if let Some(current_block) = self.block_chunks.as_mut() { - if let Some(chunk) = current_block.next() { - return Some(chunk); + if self.output_row >= self.max_output_row { + return None; + } + + if let Some(block_chunks) = self.block_chunks.as_mut() { + if let Some(mut 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(); } } - let transform = if let Some(item) = self.transform_cursor.item() { - item - } else { - return None; - }; + let transform = self.transforms.item()?; + if let Some(block) = transform.block.as_ref() { + let block_start = self.transforms.start().0 .0; + let block_end = self.transforms.end(&()).0 .0; + let start_row_in_block = self.output_row - block_start; + let end_row_in_block = cmp::min(self.max_output_row, block_end) - block_start; + self.transforms.next(&()); + let mut block_chunks = BlockChunks::new(block, start_row_in_block..end_row_in_block); + if let Some(block_chunk) = block_chunks.next() { + self.output_row += block_chunk.text.matches('\n').count() as u32; + return Some(block_chunk); + } + } - if let Some(block) = &transform.block { - let of + if self.input_chunk.text.is_empty() { + self.input_chunk = self.input_chunks.next().unwrap(); } - None + let transform_end = self.transforms.end(&()).0 .0; + let (prefix_rows, prefix_bytes) = + offset_for_row(self.input_chunk.text, transform_end - self.output_row); + self.output_row += prefix_rows; + let (prefix, suffix) = self.input_chunk.text.split_at(prefix_bytes); + + self.input_chunk.text = suffix; + Some(HighlightedChunk { + text: prefix, + ..self.input_chunk + }) } } impl<'a> BlockChunks<'a> { - fn new(block: &'a Block, range: Range) -> Self { + fn new(block: &'a Block, row_range: Range) -> Self { + let point_range = Point::new(row_range.start, 0)..Point::new(row_range.end, 0); + let offset_range = block.text.point_to_offset(point_range.start) + ..block.text.point_to_offset(point_range.end); + let mut runs = block.runs.iter().peekable(); let mut run_start = 0; while let Some((run_len, _)) = runs.peek() { let run_end = run_start + run_len; - if run_end <= range.start { + if run_end <= offset_range.start { run_start = run_end; runs.next(); } else { @@ -297,9 +343,9 @@ impl<'a> BlockChunks<'a> { Self { chunk: None, run_start, - chunks: block.text.chunks_in_range(range.clone()), + chunks: block.text.chunks_in_range(offset_range.clone()), runs, - offset: range.start, + offset: offset_range.start, } } } @@ -370,6 +416,26 @@ impl<'a> sum_tree::Dimension<'a, TransformSummary> for OutputRow { } } +// Count the number of bytes prior to a target row. +// If the string doesn't contain the target row, return the total number of rows it does contain. +// Otherwise return the target row itself. +fn offset_for_row(s: &str, target_row: u32) -> (u32, usize) { + assert!(target_row > 0); + let mut row = 0; + let mut offset = 0; + for (ix, line) in s.split('\n').enumerate() { + if ix > 0 { + row += 1; + offset += 1; + if row as u32 >= target_row { + break; + } + } + offset += line.len(); + } + (row, offset) +} + #[cfg(test)] mod tests { use super::*;