@@ -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<Buffer>,
next_block_id: AtomicUsize,
@@ -51,25 +48,27 @@ struct WrapRow(u32);
pub struct Block {
id: BlockId,
position: Anchor,
- text: Rope,
- build_runs: Mutex<Option<Arc<dyn Fn(&AppContext) -> Vec<(usize, HighlightStyle)>>>>,
- build_style: Mutex<Option<Arc<dyn Fn(&AppContext) -> BlockStyle>>>,
+ height: u8,
+ render: Mutex<Arc<dyn Fn(&BlockContext) -> ElementBox>>,
disposition: BlockDisposition,
}
#[derive(Clone)]
-pub struct BlockProperties<P, T>
+pub struct BlockProperties<P>
where
P: Clone,
- T: Clone,
{
pub position: P,
- pub text: T,
- pub build_runs: Option<Arc<dyn Fn(&AppContext) -> Vec<(usize, HighlightStyle)>>>,
- pub build_style: Option<Arc<dyn Fn(&AppContext) -> BlockStyle>>,
+ pub height: u8,
+ pub render: Arc<dyn Fn(&BlockContext) -> ElementBox>,
pub disposition: BlockDisposition,
}
+pub struct BlockContext<'a> {
+ pub cx: &'a AppContext,
+ pub anchor_x: f32,
+}
+
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum BlockDisposition {
Above,
@@ -83,7 +82,7 @@ struct Transform {
}
#[derive(Clone, Debug)]
-struct AlignedBlock {
+pub struct AlignedBlock {
block: Arc<Block>,
column: u32,
}
@@ -92,35 +91,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<BlockChunks<'a>>,
output_row: u32,
max_output_row: u32,
- cx: Option<&'a AppContext>,
-}
-
-struct BlockChunks<'a> {
- chunks: rope::Chunks<'a>,
- runs: iter::Peekable<vec::IntoIter<(usize, HighlightStyle)>>,
- 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 +317,13 @@ impl BlockMap {
*transforms = new_transforms;
}
- pub fn restyle<F1, F2>(&mut self, mut styles: HashMap<BlockId, (Option<F1>, Option<F2>)>)
+ pub fn replace<F>(&mut self, mut element_builders: HashMap<BlockId, F>)
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<dyn Fn(&AppContext) -> Vec<(usize, HighlightStyle)>>
- });
- *block.build_style.lock() = build_style.map(|build_style| {
- Arc::new(build_style) as Arc<dyn Fn(&AppContext) -> BlockStyle>
- });
+ if let Some(build_element) = element_builders.remove(&block.id) {
+ *block.render.lock() = Arc::new(build_element);
}
}
}
@@ -393,14 +371,13 @@ impl std::ops::DerefMut for BlockPoint {
}
impl<'a> BlockMapWriter<'a> {
- pub fn insert<P, T>(
+ pub fn insert<P>(
&mut self,
- blocks: impl IntoIterator<Item = BlockProperties<P, T>>,
+ blocks: impl IntoIterator<Item = BlockProperties<P>>,
cx: &AppContext,
) -> Vec<BlockId>
where
P: ToOffset + Clone,
- T: Into<Rope> + Clone,
{
let buffer = self.0.buffer.read(cx);
let mut ids = Vec::new();
@@ -436,9 +413,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 +471,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<u32>,
- theme: Option<&'a SyntaxTheme>,
- cx: Option<&'a AppContext>,
- ) -> Chunks<'a> {
+ pub fn chunks<'a>(&'a self, rows: Range<u32>, 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 +506,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 +523,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 +530,29 @@ impl BlockSnapshot {
}
}
+ pub fn blocks_in_range<'a>(
+ &'a self,
+ rows: Range<u32>,
+ ) -> impl Iterator<Item = (u32, &'a AlignedBlock)> {
+ let mut cursor = self.transforms.cursor::<BlockRow>();
+ 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 +560,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 +569,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 +673,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<Block>, 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 +701,25 @@ 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 mut block_end = self.transforms.end(&()).0 .0;
+ self.transforms.next(&());
+ if self.transforms.item().is_none() {
+ block_end -= 1;
+ }
+
let start_in_block = self.output_row - block_start;
let end_in_block = cmp::min(self.max_output_row, block_end) - block_start;
- self.transforms.next(&());
- self.block_chunks = Some(BlockChunks::new(
- block,
- start_in_block..end_in_block,
- self.cx,
- ));
- return self.next();
+ let line_count = end_in_block - start_in_block;
+ self.output_row += line_count;
+
+ 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 +756,8 @@ impl<'a> Iterator for Chunks<'a> {
}
}
-impl<'a> BlockChunks<'a> {
- fn new(block: &'a AlignedBlock, rows: Range<u32>, 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<Self::Item> {
- 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<u32>;
fn next(&mut self) -> Option<Self::Item> {
if self.started {
@@ -911,11 +771,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 +791,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 +814,24 @@ 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;
@@ -975,7 +845,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 +872,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 +892,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<u32>,
- 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();
@@ -1110,49 +909,67 @@ mod tests {
let mut block_map = BlockMap::new(buffer.clone(), wraps_snapshot.clone());
let mut writer = block_map.write(wraps_snapshot.clone(), vec![], cx);
- let block_ids = writer.insert(
+ writer.insert(
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");
+
+ let blocks = snapshot
+ .blocks_in_range(0..8)
+ .map(|(start_row, block)| {
+ (
+ start_row..start_row + block.height(),
+ block.column(),
+ block
+ .render(&BlockContext { cx, anchor_x: 0. })
+ .name()
+ .unwrap()
+ .to_string(),
+ )
+ })
+ .collect::<Vec<_>>();
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)
);
assert_eq!(
snapshot.to_block_point(WrapPoint::new(1, 0)),
- BlockPoint::new(3, 0)
+ BlockPoint::new(4, 0)
);
assert_eq!(
snapshot.to_block_point(WrapPoint::new(3, 3)),
- BlockPoint::new(5, 3)
+ BlockPoint::new(6, 3)
);
assert_eq!(
@@ -1168,7 +985,7 @@ mod tests {
WrapPoint::new(1, 0)
);
assert_eq!(
- snapshot.to_wrap_point(BlockPoint::new(6, 0)),
+ snapshot.to_wrap_point(BlockPoint::new(7, 0)),
WrapPoint::new(3, 3)
);
@@ -1178,7 +995,7 @@ mod tests {
);
assert_eq!(
snapshot.clip_point(BlockPoint::new(1, 0), Bias::Right),
- BlockPoint::new(3, 0)
+ BlockPoint::new(4, 0)
);
assert_eq!(
snapshot.clip_point(BlockPoint::new(1, 1), Bias::Left),
@@ -1186,43 +1003,46 @@ mod tests {
);
assert_eq!(
snapshot.clip_point(BlockPoint::new(1, 1), Bias::Right),
- BlockPoint::new(3, 0)
+ BlockPoint::new(4, 0)
);
assert_eq!(
- snapshot.clip_point(BlockPoint::new(3, 0), Bias::Left),
- BlockPoint::new(3, 0)
+ snapshot.clip_point(BlockPoint::new(4, 0), Bias::Left),
+ BlockPoint::new(4, 0)
);
assert_eq!(
- snapshot.clip_point(BlockPoint::new(3, 0), Bias::Right),
- BlockPoint::new(3, 0)
+ snapshot.clip_point(BlockPoint::new(4, 0), Bias::Right),
+ BlockPoint::new(4, 0)
);
assert_eq!(
- snapshot.clip_point(BlockPoint::new(5, 3), Bias::Left),
- BlockPoint::new(5, 3)
+ snapshot.clip_point(BlockPoint::new(6, 3), Bias::Left),
+ BlockPoint::new(6, 3)
);
assert_eq!(
- snapshot.clip_point(BlockPoint::new(5, 3), Bias::Right),
- BlockPoint::new(5, 3)
+ snapshot.clip_point(BlockPoint::new(6, 3), Bias::Right),
+ BlockPoint::new(6, 3)
);
assert_eq!(
- snapshot.clip_point(BlockPoint::new(6, 0), Bias::Left),
- BlockPoint::new(5, 3)
+ snapshot.clip_point(BlockPoint::new(7, 0), Bias::Left),
+ BlockPoint::new(6, 3)
);
assert_eq!(
- snapshot.clip_point(BlockPoint::new(6, 0), Bias::Right),
- BlockPoint::new(5, 3)
+ snapshot.clip_point(BlockPoint::new(7, 0), Bias::Right),
+ BlockPoint::new(6, 3)
);
assert_eq!(
- snapshot.buffer_rows(0, None).collect::<Vec<_>>(),
+ snapshot.buffer_rows(0).collect::<Vec<_>>(),
&[
- 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)
+ Some(0),
+ None,
+ None,
+ None,
+ Some(1),
+ Some(2),
+ Some(3),
+ None,
+ None,
+ None
]
);
@@ -1240,10 +1060,7 @@ 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]
@@ -1267,17 +1084,15 @@ mod tests {
vec![
BlockProperties {
position: Point::new(1, 12),
- text: "<BLOCK 1",
disposition: BlockDisposition::Above,
- build_runs: None,
- build_style: None,
+ render: Arc::new(|_| Empty::new().named("block 1")),
+ height: 1,
},
BlockProperties {
position: Point::new(1, 1),
- text: ">BLOCK 2",
disposition: BlockDisposition::Below,
- build_runs: None,
- build_style: None,
+ render: Arc::new(|_| Empty::new().named("block 2")),
+ height: 1,
},
],
cx,
@@ -1288,7 +1103,7 @@ mod tests {
let mut snapshot = block_map.read(wraps_snapshot, vec![], cx);
assert_eq!(
snapshot.text(),
- "one two \nthree\n <BLOCK 1\nfour five \nsix\n >BLOCK 2\nseven \neight"
+ "one two \nthree\n\nfour five \nsix\n\nseven \neight"
);
}
@@ -1348,33 +1163,23 @@ mod tests {
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::<String>()
- .to_uppercase()
- .as_str(),
- );
let disposition = if rng.gen() {
- text.push_front("<");
BlockDisposition::Above
} else {
- text.push_front(">");
BlockDisposition::Below
};
+ let height = rng.gen_range(1..5);
log::info!(
- "inserting block {:?} {:?} with text {:?}",
+ "inserting block {:?} {:?} with height {}",
disposition,
position.to_point(buffer),
- text.to_string()
+ height
);
BlockProperties {
position,
- text,
+ height,
disposition,
- build_runs: None,
- build_style: None,
+ render: Arc::new(|_| Empty::new().boxed()),
}
})
.collect::<Vec<_>>();
@@ -1454,10 +1259,9 @@ mod tests {
id,
BlockProperties {
position: BlockPoint::new(row, column),
- text: block.text,
- build_runs: block.build_runs.clone(),
- build_style: None,
+ height: block.height,
disposition: block.disposition,
+ render: block.render.clone(),
},
)
})
@@ -1479,17 +1283,12 @@ mod tests {
.to_point(WrapPoint::new(row, 0), Bias::Left)
.row;
- while let Some((block_id, block)) = sorted_blocks.peek() {
+ while let Some((_, 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));
+ let text = "\n".repeat(block.height as usize);
+ expected_text.push_str(&text);
+ for _ in 0..block.height {
+ expected_buffer_rows.push(None);
}
sorted_blocks.next();
} else {
@@ -1498,24 +1297,15 @@ mod tests {
}
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_buffer_rows.push(if soft_wrapped { None } else { Some(buffer_row) });
expected_text.push_str(input_line);
- while let Some((block_id, block)) = sorted_blocks.peek() {
+ while let Some((_, 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));
+ let text = "\n".repeat(block.height as usize);
+ expected_text.push_str(&text);
+ for _ in 0..block.height {
+ expected_buffer_rows.push(None);
}
sorted_blocks.next();
} else {
@@ -1529,7 +1319,7 @@ mod tests {
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)
+ .chunks(start_row as u32..expected_row_count as u32, None)
.map(|chunk| chunk.text)
.collect::<String>();
assert_eq!(
@@ -1539,7 +1329,7 @@ mod tests {
);
assert_eq!(
blocks_snapshot
- .buffer_rows(start_row as u32, None)
+ .buffer_rows(start_row as u32)
.collect::<Vec<_>>(),
&expected_buffer_rows[start_row..]
);
@@ -1569,7 +1359,6 @@ mod tests {
}
}
- log::info!("getting longest row >>>>>>>>>>>>>>>>>>>>>>>>");
let longest_row = blocks_snapshot.longest_row();
assert!(
expected_longest_rows.contains(&longest_row),
@@ -1,6 +1,8 @@
+use crate::display_map::{BlockContext, ToDisplayPoint};
+
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,11 +15,11 @@ 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;
-use language::Chunk;
+use language::{Chunk, ToPoint};
use smallvec::SmallVec;
use std::{
cmp::{self, Ordering},
@@ -25,7 +27,6 @@ use std::{
fmt::Write,
ops::Range,
};
-use theme::BlockStyle;
pub struct EditorElement {
view: WeakViewHandle<Editor>,
@@ -219,7 +220,6 @@ impl EditorElement {
) {
let bounds = gutter_bounds.union_rect(text_bounds);
let scroll_top = layout.snapshot.scroll_position().y() * layout.line_height;
- let start_row = layout.snapshot.scroll_position().y() as u32;
let editor = self.view(cx.app);
let style = &self.settings.style;
cx.scene.push_quad(Quad {
@@ -278,51 +278,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 +416,24 @@ impl EditorElement {
cx.scene.pop_layer();
}
+ fn paint_blocks(
+ &mut self,
+ text_bounds: RectF,
+ visible_bounds: RectF,
+ layout: &mut LayoutState,
+ cx: &mut PaintContext,
+ ) {
+ let scroll_position = layout.snapshot.scroll_position();
+ let scroll_left = scroll_position.x() * layout.em_width;
+ let scroll_top = scroll_position.y() * layout.line_height;
+
+ for (row, element) in &mut layout.blocks {
+ let origin = text_bounds.origin()
+ + vec2f(-scroll_left, *row as f32 * layout.line_height - scroll_top);
+ element.paint(origin, visible_bounds, cx);
+ }
+ }
+
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 +460,13 @@ impl EditorElement {
active_rows: &BTreeMap<u32, bool>,
snapshot: &Snapshot,
cx: &LayoutContext,
- ) -> (
- Vec<Option<text_layout::Line>>,
- Vec<(Range<u32>, BlockStyle)>,
- ) {
+ ) -> Vec<Option<text_layout::Line>> {
let style = &self.settings.style;
let include_line_numbers = snapshot.mode == EditorMode::Full;
- let mut last_block_id = None;
- let mut blocks = Vec::<(Range<u32>, 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 +476,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 +549,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 +619,46 @@ impl EditorElement {
layouts
}
+
+ fn layout_blocks(
+ &mut self,
+ rows: Range<u32>,
+ snapshot: &Snapshot,
+ text_width: f32,
+ line_height: f32,
+ style: &EditorStyle,
+ 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 = 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 });
+ element.layout(
+ SizeConstraint {
+ min: Vector2F::zero(),
+ max: vec2f(text_width, block.height() as f32 * line_height),
+ },
+ cx,
+ );
+ (start_row, element)
+ })
+ .collect()
+ }
}
impl Element for EditorElement {
@@ -773,8 +764,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 +774,16 @@ impl Element for EditorElement {
}
}
+ let blocks = self.layout_blocks(
+ start_row..end_row,
+ &snapshot,
+ text_size.x(),
+ line_height,
+ &style,
+ &line_layouts,
+ cx,
+ );
+
let mut layout = LayoutState {
size,
gutter_size,
@@ -797,7 +797,7 @@ impl Element for EditorElement {
highlighted_row,
line_layouts,
line_number_layouts,
- block_layouts,
+ blocks,
line_height,
em_width,
em_advance,
@@ -853,6 +853,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 +928,7 @@ pub struct LayoutState {
highlighted_row: Option<u32>,
line_layouts: Vec<text_layout::Line>,
line_number_layouts: Vec<Option<text_layout::Line>>,
- block_layouts: Vec<(Range<u32>, BlockStyle)>,
+ blocks: Vec<(u32, ElementBox)>,
line_height: f32,
em_width: f32,
em_advance: f32,
@@ -940,7 +941,8 @@ pub struct LayoutState {
impl LayoutState {
fn scroll_width(&self, layout_cache: &TextLayoutCache) -> f32 {
let row = self.snapshot.longest_row();
- let longest_line_width = self.layout_line(row, &self.snapshot, layout_cache).width();
+ let longest_line_width =
+ layout_line(row, &self.snapshot, &self.style, layout_cache).width();
longest_line_width.max(self.max_visible_line_width) + self.overscroll.x()
}
@@ -955,36 +957,36 @@ impl LayoutState {
max_row.saturating_sub(1) as f32,
)
}
+}
- pub fn layout_line(
- &self,
- row: u32,
- snapshot: &Snapshot,
- layout_cache: &TextLayoutCache,
- ) -> text_layout::Line {
- let mut line = snapshot.line(row);
-
- if line.len() > MAX_LINE_LEN {
- let mut len = MAX_LINE_LEN;
- while !line.is_char_boundary(len) {
- len -= 1;
- }
- line.truncate(len);
+fn layout_line(
+ row: u32,
+ snapshot: &Snapshot,
+ style: &EditorStyle,
+ layout_cache: &TextLayoutCache,
+) -> text_layout::Line {
+ let mut line = snapshot.line(row);
+
+ if line.len() > MAX_LINE_LEN {
+ let mut len = MAX_LINE_LEN;
+ while !line.is_char_boundary(len) {
+ len -= 1;
}
-
- layout_cache.layout_str(
- &line,
- self.style.text.font_size,
- &[(
- snapshot.line_len(row) as usize,
- RunStyle {
- font_id: self.style.text.font_id,
- color: Color::black(),
- underline: None,
- },
- )],
- )
+ line.truncate(len);
}
+
+ layout_cache.layout_str(
+ &line,
+ style.text.font_size,
+ &[(
+ snapshot.line_len(row) as usize,
+ RunStyle {
+ font_id: style.text.font_id,
+ color: Color::black(),
+ underline: None,
+ },
+ )],
+ )
}
pub struct PaintState {
@@ -1185,7 +1187,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);