Start work on allowing blocks to be styled
This commit is contained in:
parent
c04151f999
commit
8d1a4a6a24
7 changed files with 232 additions and 96 deletions
|
@ -13,7 +13,7 @@ use language::{Anchor, Buffer, Point, ToOffset, ToPoint};
|
||||||
use std::{collections::HashSet, ops::Range};
|
use std::{collections::HashSet, ops::Range};
|
||||||
use sum_tree::Bias;
|
use sum_tree::Bias;
|
||||||
use tab_map::TabMap;
|
use tab_map::TabMap;
|
||||||
use theme::SyntaxTheme;
|
use theme::{BlockStyle, SyntaxTheme};
|
||||||
use wrap_map::WrapMap;
|
use wrap_map::WrapMap;
|
||||||
|
|
||||||
pub trait ToDisplayPoint {
|
pub trait ToDisplayPoint {
|
||||||
|
@ -172,8 +172,8 @@ impl DisplayMapSnapshot {
|
||||||
self.buffer_snapshot.len() == 0
|
self.buffer_snapshot.len() == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn buffer_rows(&self, start_row: u32) -> BufferRows {
|
pub fn buffer_rows<'a>(&'a self, start_row: u32, cx: Option<&'a AppContext>) -> BufferRows<'a> {
|
||||||
self.blocks_snapshot.buffer_rows(start_row)
|
self.blocks_snapshot.buffer_rows(start_row, cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn buffer_row_count(&self) -> u32 {
|
pub fn buffer_row_count(&self) -> u32 {
|
||||||
|
@ -416,6 +416,13 @@ impl ToDisplayPoint for Anchor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub enum DisplayRow {
|
||||||
|
Buffer(u32),
|
||||||
|
Block(BlockId, Option<BlockStyle>),
|
||||||
|
Wrap,
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
use super::wrap_map::{self, Edit as WrapEdit, Snapshot as WrapSnapshot, WrapPoint};
|
use super::{
|
||||||
|
wrap_map::{self, Edit as WrapEdit, Snapshot as WrapSnapshot, WrapPoint},
|
||||||
|
BlockStyle, DisplayRow,
|
||||||
|
};
|
||||||
use buffer::{rope, Anchor, Bias, Edit, Point, Rope, ToOffset, ToPoint as _};
|
use buffer::{rope, Anchor, Bias, Edit, Point, Rope, ToOffset, ToPoint as _};
|
||||||
use gpui::{fonts::HighlightStyle, AppContext, ModelHandle};
|
use gpui::{fonts::HighlightStyle, AppContext, ModelHandle};
|
||||||
use language::{Buffer, Chunk};
|
use language::{Buffer, Chunk};
|
||||||
|
@ -45,11 +48,12 @@ struct BlockRow(u32);
|
||||||
#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
|
#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
|
||||||
struct WrapRow(u32);
|
struct WrapRow(u32);
|
||||||
|
|
||||||
struct Block {
|
pub struct Block {
|
||||||
id: BlockId,
|
id: BlockId,
|
||||||
position: Anchor,
|
position: Anchor,
|
||||||
text: Rope,
|
text: Rope,
|
||||||
build_runs: Option<Arc<dyn Fn(&AppContext) -> Vec<(usize, HighlightStyle)>>>,
|
build_runs: Option<Arc<dyn Fn(&AppContext) -> Vec<(usize, HighlightStyle)>>>,
|
||||||
|
build_style: Option<Arc<dyn Fn(&AppContext) -> BlockStyle>>,
|
||||||
disposition: BlockDisposition,
|
disposition: BlockDisposition,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,6 +66,7 @@ where
|
||||||
pub position: P,
|
pub position: P,
|
||||||
pub text: T,
|
pub text: T,
|
||||||
pub build_runs: Option<Arc<dyn Fn(&AppContext) -> Vec<(usize, HighlightStyle)>>>,
|
pub build_runs: Option<Arc<dyn Fn(&AppContext) -> Vec<(usize, HighlightStyle)>>>,
|
||||||
|
pub build_style: Option<Arc<dyn Fn(&AppContext) -> BlockStyle>>,
|
||||||
pub disposition: BlockDisposition,
|
pub disposition: BlockDisposition,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,6 +120,7 @@ pub struct BufferRows<'a> {
|
||||||
transforms: sum_tree::Cursor<'a, Transform, (BlockRow, WrapRow)>,
|
transforms: sum_tree::Cursor<'a, Transform, (BlockRow, WrapRow)>,
|
||||||
input_buffer_rows: wrap_map::BufferRows<'a>,
|
input_buffer_rows: wrap_map::BufferRows<'a>,
|
||||||
output_row: u32,
|
output_row: u32,
|
||||||
|
cx: Option<&'a AppContext>,
|
||||||
started: bool,
|
started: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -415,6 +421,7 @@ impl<'a> BlockMapWriter<'a> {
|
||||||
position,
|
position,
|
||||||
text: block.text.into(),
|
text: block.text.into(),
|
||||||
build_runs: block.build_runs,
|
build_runs: block.build_runs,
|
||||||
|
build_style: block.build_style,
|
||||||
disposition: block.disposition,
|
disposition: block.disposition,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
@ -519,7 +526,7 @@ impl BlockSnapshot {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn buffer_rows(&self, start_row: u32) -> BufferRows {
|
pub fn buffer_rows<'a>(&'a self, start_row: u32, cx: Option<&'a AppContext>) -> BufferRows<'a> {
|
||||||
let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>();
|
let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>();
|
||||||
cursor.seek(&BlockRow(start_row), Bias::Right, &());
|
cursor.seek(&BlockRow(start_row), Bias::Right, &());
|
||||||
let (output_start, input_start) = cursor.start();
|
let (output_start, input_start) = cursor.start();
|
||||||
|
@ -530,6 +537,7 @@ impl BlockSnapshot {
|
||||||
};
|
};
|
||||||
let input_start_row = input_start.0 + overshoot;
|
let input_start_row = input_start.0 + overshoot;
|
||||||
BufferRows {
|
BufferRows {
|
||||||
|
cx,
|
||||||
transforms: cursor,
|
transforms: cursor,
|
||||||
input_buffer_rows: self.wrap_snapshot.buffer_rows(input_start_row),
|
input_buffer_rows: self.wrap_snapshot.buffer_rows(input_start_row),
|
||||||
output_row: start_row,
|
output_row: start_row,
|
||||||
|
@ -871,7 +879,7 @@ impl<'a> Iterator for BlockChunks<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Iterator for BufferRows<'a> {
|
impl<'a> Iterator for BufferRows<'a> {
|
||||||
type Item = Option<u32>;
|
type Item = DisplayRow;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
if self.started {
|
if self.started {
|
||||||
|
@ -885,10 +893,13 @@ impl<'a> Iterator for BufferRows<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let transform = self.transforms.item()?;
|
let transform = self.transforms.item()?;
|
||||||
if transform.is_isomorphic() {
|
if let Some(block) = &transform.block {
|
||||||
Some(self.input_buffer_rows.next().unwrap())
|
let style = self
|
||||||
|
.cx
|
||||||
|
.and_then(|cx| block.build_style.as_ref().map(|f| f(cx)));
|
||||||
|
Some(DisplayRow::Block(block.id, style))
|
||||||
} else {
|
} else {
|
||||||
Some(None)
|
Some(self.input_buffer_rows.next().unwrap())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1006,6 +1017,7 @@ mod tests {
|
||||||
id: BlockId(0),
|
id: BlockId(0),
|
||||||
position: Anchor::min(),
|
position: Anchor::min(),
|
||||||
text: "one!\ntwo three\nfour".into(),
|
text: "one!\ntwo three\nfour".into(),
|
||||||
|
build_style: None,
|
||||||
build_runs: Some(Arc::new(move |_| {
|
build_runs: Some(Arc::new(move |_| {
|
||||||
vec![(3, red.into()), (6, Default::default()), (5, blue.into())]
|
vec![(3, red.into()), (6, Default::default()), (5, blue.into())]
|
||||||
})),
|
})),
|
||||||
|
@ -1080,25 +1092,28 @@ mod tests {
|
||||||
let mut block_map = BlockMap::new(buffer.clone(), wraps_snapshot.clone());
|
let mut block_map = BlockMap::new(buffer.clone(), wraps_snapshot.clone());
|
||||||
|
|
||||||
let mut writer = block_map.write(wraps_snapshot.clone(), vec![], cx);
|
let mut writer = block_map.write(wraps_snapshot.clone(), vec![], cx);
|
||||||
writer.insert(
|
let block_ids = writer.insert(
|
||||||
vec![
|
vec![
|
||||||
BlockProperties {
|
BlockProperties {
|
||||||
position: Point::new(1, 0),
|
position: Point::new(1, 0),
|
||||||
text: "BLOCK 1",
|
text: "BLOCK 1",
|
||||||
disposition: BlockDisposition::Above,
|
disposition: BlockDisposition::Above,
|
||||||
build_runs: None,
|
build_runs: None,
|
||||||
|
build_style: None,
|
||||||
},
|
},
|
||||||
BlockProperties {
|
BlockProperties {
|
||||||
position: Point::new(1, 2),
|
position: Point::new(1, 2),
|
||||||
text: "BLOCK 2",
|
text: "BLOCK 2",
|
||||||
disposition: BlockDisposition::Above,
|
disposition: BlockDisposition::Above,
|
||||||
build_runs: None,
|
build_runs: None,
|
||||||
|
build_style: None,
|
||||||
},
|
},
|
||||||
BlockProperties {
|
BlockProperties {
|
||||||
position: Point::new(3, 2),
|
position: Point::new(3, 2),
|
||||||
text: "BLOCK 3",
|
text: "BLOCK 3",
|
||||||
disposition: BlockDisposition::Below,
|
disposition: BlockDisposition::Below,
|
||||||
build_runs: None,
|
build_runs: None,
|
||||||
|
build_style: None,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
cx,
|
cx,
|
||||||
|
@ -1181,8 +1196,16 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
snapshot.buffer_rows(0).collect::<Vec<_>>(),
|
snapshot.buffer_rows(0, None).collect::<Vec<_>>(),
|
||||||
&[Some(0), None, None, Some(1), Some(2), Some(3), None]
|
&[
|
||||||
|
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)
|
||||||
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Insert a line break, separating two block decorations into separate
|
// Insert a line break, separating two block decorations into separate
|
||||||
|
@ -1227,12 +1250,14 @@ mod tests {
|
||||||
text: "<BLOCK 1",
|
text: "<BLOCK 1",
|
||||||
disposition: BlockDisposition::Above,
|
disposition: BlockDisposition::Above,
|
||||||
build_runs: None,
|
build_runs: None,
|
||||||
|
build_style: None,
|
||||||
},
|
},
|
||||||
BlockProperties {
|
BlockProperties {
|
||||||
position: Point::new(1, 1),
|
position: Point::new(1, 1),
|
||||||
text: ">BLOCK 2",
|
text: ">BLOCK 2",
|
||||||
disposition: BlockDisposition::Below,
|
disposition: BlockDisposition::Below,
|
||||||
build_runs: None,
|
build_runs: None,
|
||||||
|
build_style: None,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
cx,
|
cx,
|
||||||
|
@ -1325,8 +1350,9 @@ mod tests {
|
||||||
BlockProperties {
|
BlockProperties {
|
||||||
position,
|
position,
|
||||||
text,
|
text,
|
||||||
build_runs: None,
|
|
||||||
disposition,
|
disposition,
|
||||||
|
build_runs: None,
|
||||||
|
build_style: None,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
@ -1402,6 +1428,7 @@ mod tests {
|
||||||
position: BlockPoint::new(row, column),
|
position: BlockPoint::new(row, column),
|
||||||
text: block.text,
|
text: block.text,
|
||||||
build_runs: block.build_runs.clone(),
|
build_runs: block.build_runs.clone(),
|
||||||
|
build_style: None,
|
||||||
disposition: block.disposition,
|
disposition: block.disposition,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -1424,7 +1451,7 @@ mod tests {
|
||||||
.to_point(WrapPoint::new(row, 0), Bias::Left)
|
.to_point(WrapPoint::new(row, 0), Bias::Left)
|
||||||
.row;
|
.row;
|
||||||
|
|
||||||
while let Some((_, block)) = sorted_blocks.peek() {
|
while let Some((block_id, block)) = sorted_blocks.peek() {
|
||||||
if block.position.row == row && block.disposition == BlockDisposition::Above {
|
if block.position.row == row && block.disposition == BlockDisposition::Above {
|
||||||
let text = block.text.to_string();
|
let text = block.text.to_string();
|
||||||
let padding = " ".repeat(block.position.column as usize);
|
let padding = " ".repeat(block.position.column as usize);
|
||||||
|
@ -1434,7 +1461,7 @@ mod tests {
|
||||||
expected_text.push_str(line);
|
expected_text.push_str(line);
|
||||||
}
|
}
|
||||||
expected_text.push('\n');
|
expected_text.push('\n');
|
||||||
expected_buffer_rows.push(None);
|
expected_buffer_rows.push(DisplayRow::Block(*block_id, None));
|
||||||
}
|
}
|
||||||
sorted_blocks.next();
|
sorted_blocks.next();
|
||||||
} else {
|
} else {
|
||||||
|
@ -1443,10 +1470,14 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
let soft_wrapped = wraps_snapshot.to_tab_point(WrapPoint::new(row, 0)).column() > 0;
|
let soft_wrapped = wraps_snapshot.to_tab_point(WrapPoint::new(row, 0)).column() > 0;
|
||||||
expected_buffer_rows.push(if soft_wrapped { None } else { Some(buffer_row) });
|
expected_buffer_rows.push(if soft_wrapped {
|
||||||
|
DisplayRow::Wrap
|
||||||
|
} else {
|
||||||
|
DisplayRow::Buffer(buffer_row)
|
||||||
|
});
|
||||||
expected_text.push_str(input_line);
|
expected_text.push_str(input_line);
|
||||||
|
|
||||||
while let Some((_, block)) = sorted_blocks.peek() {
|
while let Some((block_id, block)) = sorted_blocks.peek() {
|
||||||
if block.position.row == row && block.disposition == BlockDisposition::Below {
|
if block.position.row == row && block.disposition == BlockDisposition::Below {
|
||||||
let text = block.text.to_string();
|
let text = block.text.to_string();
|
||||||
let padding = " ".repeat(block.position.column as usize);
|
let padding = " ".repeat(block.position.column as usize);
|
||||||
|
@ -1456,7 +1487,7 @@ mod tests {
|
||||||
expected_text.push_str(&padding);
|
expected_text.push_str(&padding);
|
||||||
expected_text.push_str(line);
|
expected_text.push_str(line);
|
||||||
}
|
}
|
||||||
expected_buffer_rows.push(None);
|
expected_buffer_rows.push(DisplayRow::Block(*block_id, None));
|
||||||
}
|
}
|
||||||
sorted_blocks.next();
|
sorted_blocks.next();
|
||||||
} else {
|
} else {
|
||||||
|
@ -1480,7 +1511,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
blocks_snapshot
|
blocks_snapshot
|
||||||
.buffer_rows(start_row as u32)
|
.buffer_rows(start_row as u32, None)
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
&expected_buffer_rows[start_row..]
|
&expected_buffer_rows[start_row..]
|
||||||
);
|
);
|
||||||
|
|
|
@ -2,6 +2,7 @@ use super::{
|
||||||
fold_map,
|
fold_map,
|
||||||
patch::Patch,
|
patch::Patch,
|
||||||
tab_map::{self, Edit as TabEdit, Snapshot as TabSnapshot, TabPoint},
|
tab_map::{self, Edit as TabEdit, Snapshot as TabSnapshot, TabPoint},
|
||||||
|
DisplayRow,
|
||||||
};
|
};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
fonts::FontId, text_layout::LineWrapper, Entity, ModelContext, ModelHandle, MutableAppContext,
|
fonts::FontId, text_layout::LineWrapper, Entity, ModelContext, ModelHandle, MutableAppContext,
|
||||||
|
@ -712,7 +713,11 @@ impl Snapshot {
|
||||||
prev_tab_row = tab_point.row();
|
prev_tab_row = tab_point.row();
|
||||||
soft_wrapped = false;
|
soft_wrapped = false;
|
||||||
}
|
}
|
||||||
expected_buffer_rows.push(if soft_wrapped { None } else { Some(buffer_row) });
|
expected_buffer_rows.push(if soft_wrapped {
|
||||||
|
DisplayRow::Wrap
|
||||||
|
} else {
|
||||||
|
DisplayRow::Buffer(buffer_row)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
for start_display_row in 0..expected_buffer_rows.len() {
|
for start_display_row in 0..expected_buffer_rows.len() {
|
||||||
|
@ -792,7 +797,7 @@ impl<'a> Iterator for Chunks<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Iterator for BufferRows<'a> {
|
impl<'a> Iterator for BufferRows<'a> {
|
||||||
type Item = Option<u32>;
|
type Item = DisplayRow;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
if self.output_row > self.max_output_row {
|
if self.output_row > self.max_output_row {
|
||||||
|
@ -812,7 +817,11 @@ impl<'a> Iterator for BufferRows<'a> {
|
||||||
self.soft_wrapped = true;
|
self.soft_wrapped = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(if soft_wrapped { None } else { Some(buffer_row) })
|
Some(if soft_wrapped {
|
||||||
|
DisplayRow::Wrap
|
||||||
|
} else {
|
||||||
|
DisplayRow::Buffer(buffer_row)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use super::{
|
use super::{
|
||||||
DisplayPoint, Editor, EditorMode, EditorSettings, EditorStyle, Input, Scroll, Select,
|
DisplayPoint, DisplayRow, Editor, EditorMode, EditorSettings, EditorStyle, Input, Scroll,
|
||||||
SelectPhase, Snapshot, MAX_LINE_LEN,
|
Select, SelectPhase, Snapshot, MAX_LINE_LEN,
|
||||||
};
|
};
|
||||||
use clock::ReplicaId;
|
use clock::ReplicaId;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
|
@ -25,6 +25,7 @@ use std::{
|
||||||
fmt::Write,
|
fmt::Write,
|
||||||
ops::Range,
|
ops::Range,
|
||||||
};
|
};
|
||||||
|
use theme::BlockStyle;
|
||||||
|
|
||||||
pub struct EditorElement {
|
pub struct EditorElement {
|
||||||
view: WeakViewHandle<Editor>,
|
view: WeakViewHandle<Editor>,
|
||||||
|
@ -359,6 +360,30 @@ impl EditorElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(visible_text_bounds) = bounds.intersection(visible_bounds) {
|
if let Some(visible_text_bounds) = bounds.intersection(visible_bounds) {
|
||||||
|
// Draw blocks
|
||||||
|
for (ixs, block_style) in &layout.block_layouts {
|
||||||
|
let row = start_row + ixs.start;
|
||||||
|
let origin = content_origin
|
||||||
|
+ vec2f(-scroll_left, 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(origin, vec2f(visible_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.,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Draw glyphs
|
// Draw glyphs
|
||||||
for (ix, line) in layout.line_layouts.iter().enumerate() {
|
for (ix, line) in layout.line_layouts.iter().enumerate() {
|
||||||
let row = start_row + ix as u32;
|
let row = start_row + ix as u32;
|
||||||
|
@ -401,18 +426,24 @@ impl EditorElement {
|
||||||
.width()
|
.width()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout_line_numbers(
|
fn layout_rows(
|
||||||
&self,
|
&self,
|
||||||
rows: Range<u32>,
|
rows: Range<u32>,
|
||||||
active_rows: &BTreeMap<u32, bool>,
|
active_rows: &BTreeMap<u32, bool>,
|
||||||
snapshot: &Snapshot,
|
snapshot: &Snapshot,
|
||||||
cx: &LayoutContext,
|
cx: &LayoutContext,
|
||||||
) -> Vec<Option<text_layout::Line>> {
|
) -> (
|
||||||
|
Vec<Option<text_layout::Line>>,
|
||||||
|
Vec<(Range<u32>, BlockStyle)>,
|
||||||
|
) {
|
||||||
let style = &self.settings.style;
|
let style = &self.settings.style;
|
||||||
let mut layouts = Vec::with_capacity(rows.len());
|
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();
|
let mut line_number = String::new();
|
||||||
for (ix, buffer_row) in snapshot
|
for (ix, row) in snapshot
|
||||||
.buffer_rows(rows.start)
|
.buffer_rows(rows.start, cx)
|
||||||
.take((rows.end - rows.start) as usize)
|
.take((rows.end - rows.start) as usize)
|
||||||
.enumerate()
|
.enumerate()
|
||||||
{
|
{
|
||||||
|
@ -422,27 +453,46 @@ impl EditorElement {
|
||||||
} else {
|
} else {
|
||||||
style.line_number
|
style.line_number
|
||||||
};
|
};
|
||||||
if let Some(buffer_row) = buffer_row {
|
match row {
|
||||||
line_number.clear();
|
DisplayRow::Buffer(buffer_row) => {
|
||||||
write!(&mut line_number, "{}", buffer_row + 1).unwrap();
|
if include_line_numbers {
|
||||||
layouts.push(Some(cx.text_layout_cache.layout_str(
|
line_number.clear();
|
||||||
&line_number,
|
write!(&mut line_number, "{}", buffer_row + 1).unwrap();
|
||||||
style.text.font_size,
|
line_number_layouts.push(Some(cx.text_layout_cache.layout_str(
|
||||||
&[(
|
&line_number,
|
||||||
line_number.len(),
|
style.text.font_size,
|
||||||
RunStyle {
|
&[(
|
||||||
font_id: style.text.font_id,
|
line_number.len(),
|
||||||
color,
|
RunStyle {
|
||||||
underline: None,
|
font_id: style.text.font_id,
|
||||||
},
|
color,
|
||||||
)],
|
underline: None,
|
||||||
)));
|
},
|
||||||
} else {
|
)],
|
||||||
layouts.push(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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
layouts
|
(line_number_layouts, blocks)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout_lines(
|
fn layout_lines(
|
||||||
|
@ -541,7 +591,7 @@ impl EditorElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
let underline = if let Some(severity) = chunk.diagnostic {
|
let underline = if let Some(severity) = chunk.diagnostic {
|
||||||
Some(super::diagnostic_color(severity, style))
|
Some(super::diagnostic_style(severity, style).text)
|
||||||
} else {
|
} else {
|
||||||
highlight_style.underline
|
highlight_style.underline
|
||||||
};
|
};
|
||||||
|
@ -669,11 +719,8 @@ impl Element for EditorElement {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let line_number_layouts = if snapshot.mode == EditorMode::Full {
|
let (line_number_layouts, block_layouts) =
|
||||||
self.layout_line_numbers(start_row..end_row, &active_rows, &snapshot, cx)
|
self.layout_rows(start_row..end_row, &active_rows, &snapshot, cx);
|
||||||
} else {
|
|
||||||
Vec::new()
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut max_visible_line_width = 0.0;
|
let mut max_visible_line_width = 0.0;
|
||||||
let line_layouts = self.layout_lines(start_row..end_row, &mut snapshot, cx);
|
let line_layouts = self.layout_lines(start_row..end_row, &mut snapshot, cx);
|
||||||
|
@ -695,6 +742,7 @@ impl Element for EditorElement {
|
||||||
active_rows,
|
active_rows,
|
||||||
line_layouts,
|
line_layouts,
|
||||||
line_number_layouts,
|
line_number_layouts,
|
||||||
|
block_layouts,
|
||||||
line_height,
|
line_height,
|
||||||
em_width,
|
em_width,
|
||||||
selections,
|
selections,
|
||||||
|
@ -817,6 +865,7 @@ pub struct LayoutState {
|
||||||
active_rows: BTreeMap<u32, bool>,
|
active_rows: BTreeMap<u32, bool>,
|
||||||
line_layouts: Vec<text_layout::Line>,
|
line_layouts: Vec<text_layout::Line>,
|
||||||
line_number_layouts: Vec<Option<text_layout::Line>>,
|
line_number_layouts: Vec<Option<text_layout::Line>>,
|
||||||
|
block_layouts: Vec<(Range<u32>, BlockStyle)>,
|
||||||
line_height: f32,
|
line_height: f32,
|
||||||
em_width: f32,
|
em_width: f32,
|
||||||
selections: HashMap<ReplicaId, Vec<Range<DisplayPoint>>>,
|
selections: HashMap<ReplicaId, Vec<Range<DisplayPoint>>>,
|
||||||
|
@ -1071,11 +1120,11 @@ mod tests {
|
||||||
});
|
});
|
||||||
let element = EditorElement::new(editor.downgrade(), settings);
|
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 snapshot = editor.snapshot(cx);
|
||||||
let mut presenter = cx.build_presenter(window_id, 30.);
|
let mut presenter = cx.build_presenter(window_id, 30.);
|
||||||
let mut layout_cx = presenter.build_layout_context(false, cx);
|
let mut layout_cx = presenter.build_layout_context(false, cx);
|
||||||
element.layout_line_numbers(0..6, &Default::default(), &snapshot, &mut layout_cx)
|
element.layout_rows(0..6, &Default::default(), &snapshot, &mut layout_cx)
|
||||||
});
|
});
|
||||||
assert_eq!(layouts.len(), 6);
|
assert_eq!(layouts.len(), 6);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,12 +7,11 @@ mod test;
|
||||||
|
|
||||||
use buffer::rope::TextDimension;
|
use buffer::rope::TextDimension;
|
||||||
use clock::ReplicaId;
|
use clock::ReplicaId;
|
||||||
pub use display_map::DisplayPoint;
|
|
||||||
use display_map::*;
|
use display_map::*;
|
||||||
|
pub use display_map::{DisplayPoint, DisplayRow};
|
||||||
pub use element::*;
|
pub use element::*;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
action,
|
action,
|
||||||
color::Color,
|
|
||||||
geometry::vector::{vec2f, Vector2F},
|
geometry::vector::{vec2f, Vector2F},
|
||||||
keymap::Binding,
|
keymap::Binding,
|
||||||
text_layout, AppContext, ClipboardItem, Element, ElementBox, Entity, ModelHandle,
|
text_layout, AppContext, ClipboardItem, Element, ElementBox, Entity, ModelHandle,
|
||||||
|
@ -33,7 +32,7 @@ use std::{
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
use sum_tree::Bias;
|
use sum_tree::Bias;
|
||||||
use theme::{EditorStyle, SyntaxTheme};
|
use theme::{DiagnosticStyle, EditorStyle, SyntaxTheme};
|
||||||
use util::post_inc;
|
use util::post_inc;
|
||||||
|
|
||||||
const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
|
const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
|
||||||
|
@ -342,6 +341,7 @@ struct BracketPairState {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct ActiveDiagnosticGroup {
|
struct ActiveDiagnosticGroup {
|
||||||
primary_range: Range<Anchor>,
|
primary_range: Range<Anchor>,
|
||||||
|
group_range: Range<Anchor>,
|
||||||
block_ids: HashSet<BlockId>,
|
block_ids: HashSet<BlockId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2238,21 +2238,34 @@ impl Editor {
|
||||||
loop {
|
loop {
|
||||||
let next_group = buffer
|
let next_group = buffer
|
||||||
.diagnostics_in_range::<_, usize>(search_start..buffer.len())
|
.diagnostics_in_range::<_, usize>(search_start..buffer.len())
|
||||||
.filter(|(_, diagnostic)| diagnostic.is_primary)
|
.find_map(|(range, diagnostic)| {
|
||||||
.skip_while(|(range, _)| {
|
if diagnostic.is_primary
|
||||||
Some(range.end) == active_primary_range.as_ref().map(|r| *r.end())
|
&& Some(range.end) != active_primary_range.as_ref().map(|r| *r.end())
|
||||||
})
|
{
|
||||||
.next()
|
Some((range, diagnostic.group_id))
|
||||||
.map(|(range, diagnostic)| (range, diagnostic.group_id));
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if let Some((primary_range, group_id)) = next_group {
|
if let Some((primary_range, group_id)) = next_group {
|
||||||
self.dismiss_diagnostics(cx);
|
self.dismiss_diagnostics(cx);
|
||||||
self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
|
self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
|
||||||
let buffer = self.buffer.read(cx);
|
let buffer = self.buffer.read(cx);
|
||||||
|
|
||||||
|
let mut group_end = Point::zero();
|
||||||
let diagnostic_group = buffer
|
let diagnostic_group = buffer
|
||||||
.diagnostic_group::<Point>(group_id)
|
.diagnostic_group::<Point>(group_id)
|
||||||
.map(|(range, diagnostic)| (range, diagnostic.clone()))
|
.map(|(range, diagnostic)| {
|
||||||
|
if range.end > group_end {
|
||||||
|
group_end = range.end;
|
||||||
|
}
|
||||||
|
(range, diagnostic.clone())
|
||||||
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let group_range = buffer.anchor_after(diagnostic_group[0].0.start)
|
||||||
|
..buffer.anchor_before(group_end);
|
||||||
let primary_range = buffer.anchor_after(primary_range.start)
|
let primary_range = buffer.anchor_after(primary_range.start)
|
||||||
..buffer.anchor_before(primary_range.end);
|
..buffer.anchor_before(primary_range.end);
|
||||||
|
|
||||||
|
@ -2265,12 +2278,24 @@ impl Editor {
|
||||||
BlockProperties {
|
BlockProperties {
|
||||||
position: range.start,
|
position: range.start,
|
||||||
text: diagnostic.message.as_str(),
|
text: diagnostic.message.as_str(),
|
||||||
build_runs: Some(Arc::new(move |cx| {
|
build_runs: Some(Arc::new({
|
||||||
let settings = build_settings.borrow()(cx);
|
let build_settings = build_settings.clone();
|
||||||
vec![(
|
move |cx| {
|
||||||
message_len,
|
let settings = build_settings.borrow()(cx);
|
||||||
diagnostic_color(severity, &settings.style).into(),
|
vec![(
|
||||||
)]
|
message_len,
|
||||||
|
diagnostic_style(severity, &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, &settings.style).block
|
||||||
|
}
|
||||||
})),
|
})),
|
||||||
disposition: BlockDisposition::Below,
|
disposition: BlockDisposition::Below,
|
||||||
}
|
}
|
||||||
|
@ -2282,6 +2307,7 @@ impl Editor {
|
||||||
|
|
||||||
Some(ActiveDiagnosticGroup {
|
Some(ActiveDiagnosticGroup {
|
||||||
primary_range,
|
primary_range,
|
||||||
|
group_range,
|
||||||
block_ids,
|
block_ids,
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
@ -2815,8 +2841,8 @@ impl Snapshot {
|
||||||
self.display_snapshot.buffer_row_count()
|
self.display_snapshot.buffer_row_count()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn buffer_rows(&self, start_row: u32) -> BufferRows {
|
pub fn buffer_rows<'a>(&'a self, start_row: u32, cx: &'a AppContext) -> BufferRows<'a> {
|
||||||
self.display_snapshot.buffer_rows(start_row)
|
self.display_snapshot.buffer_rows(start_row, Some(cx))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn chunks<'a>(
|
pub fn chunks<'a>(
|
||||||
|
@ -2893,10 +2919,10 @@ impl EditorSettings {
|
||||||
selection: Default::default(),
|
selection: Default::default(),
|
||||||
guest_selections: Default::default(),
|
guest_selections: Default::default(),
|
||||||
syntax: Default::default(),
|
syntax: Default::default(),
|
||||||
error_color: Default::default(),
|
diagnostic_error: Default::default(),
|
||||||
warning_color: Default::default(),
|
diagnostic_warning: Default::default(),
|
||||||
information_color: Default::default(),
|
diagnostic_information: Default::default(),
|
||||||
hint_color: Default::default(),
|
diagnostic_hint: Default::default(),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -3020,13 +3046,13 @@ impl SelectionExt for Selection<Point> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn diagnostic_color(severity: DiagnosticSeverity, style: &EditorStyle) -> Color {
|
pub fn diagnostic_style(severity: DiagnosticSeverity, style: &EditorStyle) -> DiagnosticStyle {
|
||||||
match severity {
|
match severity {
|
||||||
DiagnosticSeverity::ERROR => style.error_color,
|
DiagnosticSeverity::ERROR => style.diagnostic_error,
|
||||||
DiagnosticSeverity::WARNING => style.warning_color,
|
DiagnosticSeverity::WARNING => style.diagnostic_warning,
|
||||||
DiagnosticSeverity::INFORMATION => style.information_color,
|
DiagnosticSeverity::INFORMATION => style.diagnostic_information,
|
||||||
DiagnosticSeverity::HINT => style.hint_color,
|
DiagnosticSeverity::HINT => style.diagnostic_hint,
|
||||||
_ => style.text.color,
|
_ => Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -227,12 +227,19 @@ pub struct EditorStyle {
|
||||||
pub line_number_active: Color,
|
pub line_number_active: Color,
|
||||||
pub guest_selections: Vec<SelectionStyle>,
|
pub guest_selections: Vec<SelectionStyle>,
|
||||||
pub syntax: Arc<SyntaxTheme>,
|
pub syntax: Arc<SyntaxTheme>,
|
||||||
pub error_color: Color,
|
pub diagnostic_error: DiagnosticStyle,
|
||||||
pub warning_color: Color,
|
pub diagnostic_warning: DiagnosticStyle,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub information_color: Color,
|
pub diagnostic_information: DiagnosticStyle,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub hint_color: Color,
|
pub diagnostic_hint: DiagnosticStyle,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Deserialize, Default)]
|
||||||
|
pub struct DiagnosticStyle {
|
||||||
|
pub text: Color,
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub block: BlockStyle,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Default, Deserialize)]
|
#[derive(Clone, Copy, Default, Deserialize)]
|
||||||
|
@ -251,6 +258,12 @@ pub struct InputEditorStyle {
|
||||||
pub selection: SelectionStyle,
|
pub selection: SelectionStyle,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Eq)]
|
||||||
|
pub struct BlockStyle {
|
||||||
|
pub background: Option<Color>,
|
||||||
|
pub border: Option<Color>,
|
||||||
|
}
|
||||||
|
|
||||||
impl EditorStyle {
|
impl EditorStyle {
|
||||||
pub fn placeholder_text(&self) -> &TextStyle {
|
pub fn placeholder_text(&self) -> &TextStyle {
|
||||||
self.placeholder_text.as_ref().unwrap_or(&self.text)
|
self.placeholder_text.as_ref().unwrap_or(&self.text)
|
||||||
|
@ -273,10 +286,10 @@ impl InputEditorStyle {
|
||||||
line_number_active: Default::default(),
|
line_number_active: Default::default(),
|
||||||
guest_selections: Default::default(),
|
guest_selections: Default::default(),
|
||||||
syntax: Default::default(),
|
syntax: Default::default(),
|
||||||
error_color: Default::default(),
|
diagnostic_error: Default::default(),
|
||||||
warning_color: Default::default(),
|
diagnostic_warning: Default::default(),
|
||||||
information_color: Default::default(),
|
diagnostic_information: Default::default(),
|
||||||
hint_color: Default::default(),
|
diagnostic_hint: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -173,7 +173,7 @@ corner_radius = 6
|
||||||
|
|
||||||
[project_panel]
|
[project_panel]
|
||||||
extends = "$panel"
|
extends = "$panel"
|
||||||
padding.top = 6 # ($workspace.tab.height - $project_panel.entry.height) / 2
|
padding.top = 6 # ($workspace.tab.height - $project_panel.entry.height) / 2
|
||||||
|
|
||||||
[project_panel.entry]
|
[project_panel.entry]
|
||||||
text = "$text.1"
|
text = "$text.1"
|
||||||
|
@ -236,6 +236,7 @@ line_number_active = "$text.0.color"
|
||||||
selection = "$selection.host"
|
selection = "$selection.host"
|
||||||
guest_selections = "$selection.guests"
|
guest_selections = "$selection.guests"
|
||||||
error_color = "$status.bad"
|
error_color = "$status.bad"
|
||||||
warning_color = "$status.warn"
|
diagnostic_error = { text = "$status.bad", border = "#ff0000", background = "#ffdddd" }
|
||||||
info_color = "$status.info"
|
diagnostic_warning = { text = "$status.warn", border = "#ffff00", background = "#ffffdd" }
|
||||||
hint_color = "$status.info"
|
diagnostic_info = { text = "$status.info" }
|
||||||
|
diagnostic_hint = { text = "$status.info" }
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue