Start work on allowing blocks to be styled

This commit is contained in:
Max Brunsfeld 2021-11-18 18:16:35 -08:00
parent c04151f999
commit 8d1a4a6a24
7 changed files with 232 additions and 96 deletions

View file

@ -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::*;

View file

@ -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..]
); );

View file

@ -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)
})
} }
} }

View file

@ -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);
} }

View file

@ -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(),
} }
} }

View file

@ -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(),
} }
} }
} }

View file

@ -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" }