Merge pull request #273 from zed-industries/flexible-blocks

Render blocks as arbitrary elements
This commit is contained in:
Max Brunsfeld
2021-12-01 17:18:45 -08:00
committed by GitHub
7 changed files with 374 additions and 664 deletions
+22 -27
View File
@@ -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<P, T>(
pub fn insert_blocks<P>(
&mut self,
blocks: impl IntoIterator<Item = BlockProperties<P, T>>,
blocks: impl IntoIterator<Item = BlockProperties<P>>,
cx: &mut ModelContext<Self>,
) -> Vec<BlockId>
where
P: ToOffset + Clone,
T: Into<Rope> + 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<F1, F2>(&mut self, styles: HashMap<BlockId, (Option<F1>, Option<F2>)>)
pub fn replace_blocks<F>(&mut self, styles: HashMap<BlockId, F>)
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<BlockId>, cx: &mut ModelContext<Self>) {
@@ -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<Item = &str> {
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<u32>,
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<Item = char> + 'a {
@@ -322,6 +317,13 @@ impl DisplayMapSnapshot {
self.folds_snapshot.folds_in_range(range)
}
pub fn blocks_in_range<'a>(
&'a self,
rows: Range<u32>,
) -> impl Iterator<Item = (u32, &'a AlignedBlock)> {
self.blocks_snapshot.blocks_in_range(rows)
}
pub fn intersects_fold<T: ToOffset>(&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<BlockStyle>),
Wrap,
}
#[cfg(test)]
mod tests {
use super::*;
@@ -1065,7 +1060,7 @@ mod tests {
) -> Vec<(String, Option<Color>)> {
let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
let mut chunks: Vec<(String, Option<Color>)> = 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 {
+176 -387
View File
@@ -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),
+3 -19
View File
@@ -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<u32> {
let mut cursor = self.transforms.cursor::<WrapPoint>();
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<u32>;
fn next(&mut self) -> Option<Self::Item> {
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) })
}
}
+40 -94
View File
@@ -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, is_valid, cx.anchor_x)
});
}
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, true, cx.anchor_x)
}),
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<u32>,
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,20 @@ impl SelectionExt for Selection<Point> {
}
}
fn render_diagnostic(
diagnostic: Diagnostic,
style: &EditorStyle,
valid: bool,
anchor_x: f32,
) -> ElementBox {
let mut text_style = style.text.clone();
text_style.color = diagnostic_style(diagnostic.severity, valid, &style).text;
Text::new(diagnostic.message, text_style)
.contained()
.with_margin_left(anchor_x)
.boxed()
}
pub fn diagnostic_style(
severity: DiagnosticSeverity,
valid: bool,
+129 -127
View File
@@ -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);
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;
}
line.truncate(len);
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);
+4
View File
@@ -301,6 +301,10 @@ impl<T: Element> Default for Lifecycle<T> {
}
impl ElementBox {
pub fn name(&self) -> Option<&str> {
self.0.name.as_deref()
}
pub fn metadata<T: 'static>(&self) -> Option<&T> {
let element = unsafe { &*self.0.element.as_ptr() };
element.metadata().and_then(|m| m.downcast_ref())
-10
View File
@@ -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<Color>,
pub border: Option<Color>,
pub gutter_background: Option<Color>,
pub gutter_border: Option<Color>,
}
impl EditorStyle {
pub fn placeholder_text(&self) -> &TextStyle {
self.placeholder_text.as_ref().unwrap_or(&self.text)