Merge pull request #273 from zed-industries/flexible-blocks
Render blocks as arbitrary elements
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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,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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user