diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index 3354d76e61..4fc3af636d 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -8,7 +8,7 @@ pub use block_map::{BlockDisposition, BlockId, BlockProperties, BufferRows, Chun use block_map::{BlockMap, BlockPoint}; use buffer::Rope; use fold_map::{FoldMap, ToFoldPoint as _}; -use gpui::{fonts::FontId, Entity, ModelContext, ModelHandle}; +use gpui::{fonts::FontId, AppContext, Entity, ModelContext, ModelHandle}; use language::{Anchor, Buffer, Point, ToOffset, ToPoint}; use std::{collections::HashSet, ops::Range}; use sum_tree::Bias; @@ -230,7 +230,7 @@ impl DisplayMapSnapshot { pub fn text_chunks(&self, display_row: u32) -> impl Iterator { self.blocks_snapshot - .chunks(display_row..self.max_point().row() + 1, None) + .chunks(display_row..self.max_point().row() + 1, None, None) .map(|h| h.text) } @@ -238,8 +238,9 @@ impl DisplayMapSnapshot { &'a self, display_rows: Range, theme: Option<&'a SyntaxTheme>, + cx: &'a AppContext, ) -> block_map::Chunks<'a> { - self.blocks_snapshot.chunks(display_rows, theme) + self.blocks_snapshot.chunks(display_rows, theme, Some(cx)) } pub fn chars_at<'a>(&'a self, point: DisplayPoint) -> impl Iterator + 'a { @@ -1025,7 +1026,7 @@ mod tests { ) -> Vec<(String, Option)> { let snapshot = map.update(cx, |map, cx| map.snapshot(cx)); let mut chunks: Vec<(String, Option)> = Vec::new(); - for chunk in snapshot.chunks(rows, Some(theme)) { + for chunk in snapshot.chunks(rows, Some(theme), cx) { let color = chunk.highlight_style.map(|s| s.color); if let Some((last_chunk, last_color)) = chunks.last_mut() { if color == *last_color { diff --git a/crates/editor/src/display_map/block_map.rs b/crates/editor/src/display_map/block_map.rs index c95520a41d..df45e2932e 100644 --- a/crates/editor/src/display_map/block_map.rs +++ b/crates/editor/src/display_map/block_map.rs @@ -6,13 +6,14 @@ use parking_lot::Mutex; use std::{ cmp::{self, Ordering}, collections::HashSet, + fmt::Debug, iter, ops::Range, - slice, sync::{ atomic::{AtomicUsize, Ordering::SeqCst}, Arc, }, + vec, }; use sum_tree::SumTree; use theme::SyntaxTheme; @@ -44,12 +45,11 @@ struct BlockRow(u32); #[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)] struct WrapRow(u32); -#[derive(Debug)] struct Block { id: BlockId, position: Anchor, text: Rope, - runs: Vec<(usize, HighlightStyle)>, + build_runs: Option Vec<(usize, HighlightStyle)>>>, disposition: BlockDisposition, } @@ -61,7 +61,7 @@ where { pub position: P, pub text: T, - pub runs: Vec<(usize, HighlightStyle)>, + pub build_runs: Option Vec<(usize, HighlightStyle)>>>, pub disposition: BlockDisposition, } @@ -92,11 +92,12 @@ pub struct Chunks<'a> { block_chunks: Option>, output_row: u32, max_output_row: u32, + cx: Option<&'a AppContext>, } struct BlockChunks<'a> { chunks: rope::Chunks<'a>, - runs: iter::Peekable>, + runs: iter::Peekable>, chunk: Option<&'a str>, run_start: usize, offset: usize, @@ -403,7 +404,7 @@ impl<'a> BlockMapWriter<'a> { id, position, text: block.text.into(), - runs: block.runs, + build_runs: block.build_runs, disposition: block.disposition, }), ); @@ -460,12 +461,17 @@ impl<'a> BlockMapWriter<'a> { impl BlockSnapshot { #[cfg(test)] fn text(&mut self) -> String { - self.chunks(0..self.transforms.summary().output_rows, None) + self.chunks(0..self.transforms.summary().output_rows, None, None) .map(|chunk| chunk.text) .collect() } - pub fn chunks<'a>(&'a self, rows: Range, theme: Option<&'a SyntaxTheme>) -> Chunks<'a> { + pub fn chunks<'a>( + &'a self, + rows: Range, + theme: Option<&'a SyntaxTheme>, + cx: Option<&'a AppContext>, + ) -> 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 = { @@ -499,6 +505,7 @@ impl BlockSnapshot { transforms: cursor, output_row: rows.start, max_output_row, + cx, } } @@ -709,7 +716,11 @@ impl<'a> Iterator for Chunks<'a> { 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.block_chunks = Some(BlockChunks::new( + block, + start_in_block..end_in_block, + self.cx, + )); return self.next(); } @@ -748,11 +759,18 @@ impl<'a> Iterator for Chunks<'a> { } impl<'a> BlockChunks<'a> { - fn new(block: &'a Block, rows: Range) -> Self { + fn new(block: &'a Block, rows: Range, 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.runs.iter().peekable(); + let mut runs = block + .build_runs + .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; @@ -874,6 +892,17 @@ impl BlockDisposition { } } +impl Debug for Block { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Block") + .field("id", &self.id) + .field("position", &self.position) + .field("text", &self.text) + .field("disposition", &self.disposition) + .finish() + } +} + // Count the number of bytes prior to a target point. If the string doesn't contain the target // point, return its total extent. Otherwise return the target point itself. fn offset_for_row(s: &str, target: u32) -> (u32, usize) { @@ -938,19 +967,19 @@ mod tests { position: Point::new(1, 0), text: "BLOCK 1", disposition: BlockDisposition::Above, - runs: vec![], + build_runs: None, }, BlockProperties { position: Point::new(1, 2), text: "BLOCK 2", disposition: BlockDisposition::Above, - runs: vec![], + build_runs: None, }, BlockProperties { position: Point::new(3, 2), text: "BLOCK 3", disposition: BlockDisposition::Below, - runs: vec![], + build_runs: None, }, ], cx, @@ -1078,13 +1107,13 @@ mod tests { position: Point::new(1, 12), text: "BLOCK 2", disposition: BlockDisposition::Below, - runs: vec![], + build_runs: None, }, ], cx, @@ -1177,7 +1206,7 @@ mod tests { BlockProperties { position, text, - runs: Vec::<(usize, HighlightStyle)>::new(), + build_runs: None, disposition, } }) @@ -1252,7 +1281,7 @@ mod tests { BlockProperties { position: row, text: block.text, - runs: block.runs, + build_runs: block.build_runs.clone(), disposition: block.disposition, }, ) @@ -1313,7 +1342,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) + .chunks(start_row as u32..expected_row_count as u32, None, None) .map(|chunk| chunk.text) .collect::(); assert_eq!( diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 6e186ef9af..15b89b3f58 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -17,7 +17,7 @@ use gpui::{ MutableAppContext, PaintContext, Quad, Scene, SizeConstraint, ViewContext, WeakViewHandle, }; use json::json; -use language::{Chunk, DiagnosticSeverity}; +use language::Chunk; use smallvec::SmallVec; use std::{ cmp::{self, Ordering}, @@ -493,7 +493,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)); + let chunks = snapshot.chunks(rows.clone(), Some(&style.syntax), cx); let newline_chunk = Chunk { text: "\n", @@ -541,13 +541,7 @@ impl EditorElement { } let underline = if let Some(severity) = chunk.diagnostic { - match severity { - DiagnosticSeverity::ERROR => Some(style.error_underline), - DiagnosticSeverity::WARNING => Some(style.warning_underline), - DiagnosticSeverity::INFORMATION => Some(style.information_underline), - DiagnosticSeverity::HINT => Some(style.hint_underline), - _ => highlight_style.underline, - } + Some(super::diagnostic_color(severity, style)) } else { highlight_style.underline }; diff --git a/crates/editor/src/lib.rs b/crates/editor/src/lib.rs index e5f8d101c5..81a890d71f 100644 --- a/crates/editor/src/lib.rs +++ b/crates/editor/src/lib.rs @@ -12,6 +12,7 @@ use display_map::*; pub use element::*; use gpui::{ action, + color::Color, geometry::vector::{vec2f, Vector2F}, keymap::Binding, text_layout, AppContext, ClipboardItem, Element, ElementBox, Entity, ModelHandle, @@ -2250,21 +2251,30 @@ impl Editor { let buffer = self.buffer.read(cx); let diagnostic_group = buffer .diagnostic_group::(group_id) - .map(|(range, diagnostic)| (range, diagnostic.message.clone())) + .map(|(range, diagnostic)| (range, diagnostic.clone())) .collect::>(); let primary_range = buffer.anchor_after(primary_range.start) ..buffer.anchor_before(primary_range.end); let block_ids = display_map .insert_blocks( - diagnostic_group - .iter() - .map(|(range, message)| BlockProperties { + diagnostic_group.iter().map(|(range, diagnostic)| { + let build_settings = self.build_settings.clone(); + let message_len = diagnostic.message.len(); + let severity = diagnostic.severity; + BlockProperties { position: range.start, - text: message.as_str(), - runs: vec![], - disposition: BlockDisposition::Above, - }), + text: diagnostic.message.as_str(), + build_runs: Some(Arc::new(move |cx| { + let settings = build_settings.borrow()(cx); + vec![( + message_len, + diagnostic_color(severity, &settings.style).into(), + )] + })), + disposition: BlockDisposition::Below, + } + }), cx, ) .into_iter() @@ -2813,8 +2823,9 @@ impl Snapshot { &'a self, display_rows: Range, theme: Option<&'a SyntaxTheme>, + cx: &'a AppContext, ) -> display_map::Chunks<'a> { - self.display_snapshot.chunks(display_rows, theme) + self.display_snapshot.chunks(display_rows, theme, cx) } pub fn scroll_position(&self) -> Vector2F { @@ -2882,10 +2893,10 @@ impl EditorSettings { selection: Default::default(), guest_selections: Default::default(), syntax: Default::default(), - error_underline: Default::default(), - warning_underline: Default::default(), - information_underline: Default::default(), - hint_underline: Default::default(), + error_color: Default::default(), + warning_color: Default::default(), + information_color: Default::default(), + hint_color: Default::default(), } }, } @@ -3009,6 +3020,16 @@ impl SelectionExt for Selection { } } +pub fn diagnostic_color(severity: DiagnosticSeverity, style: &EditorStyle) -> Color { + match severity { + DiagnosticSeverity::ERROR => style.error_color, + DiagnosticSeverity::WARNING => style.warning_color, + DiagnosticSeverity::INFORMATION => style.information_color, + DiagnosticSeverity::HINT => style.hint_color, + _ => style.text.color, + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/theme/src/lib.rs b/crates/theme/src/lib.rs index c4fbcc3031..ab451b0141 100644 --- a/crates/theme/src/lib.rs +++ b/crates/theme/src/lib.rs @@ -227,12 +227,12 @@ pub struct EditorStyle { pub line_number_active: Color, pub guest_selections: Vec, pub syntax: Arc, - pub error_underline: Color, - pub warning_underline: Color, + pub error_color: Color, + pub warning_color: Color, #[serde(default)] - pub information_underline: Color, + pub information_color: Color, #[serde(default)] - pub hint_underline: Color, + pub hint_color: Color, } #[derive(Clone, Copy, Default, Deserialize)] @@ -273,10 +273,10 @@ impl InputEditorStyle { line_number_active: Default::default(), guest_selections: Default::default(), syntax: Default::default(), - error_underline: Default::default(), - warning_underline: Default::default(), - information_underline: Default::default(), - hint_underline: Default::default(), + error_color: Default::default(), + warning_color: Default::default(), + information_color: Default::default(), + hint_color: Default::default(), } } } diff --git a/crates/zed/assets/themes/_base.toml b/crates/zed/assets/themes/_base.toml index 77167df8b0..609228d86d 100644 --- a/crates/zed/assets/themes/_base.toml +++ b/crates/zed/assets/themes/_base.toml @@ -173,7 +173,7 @@ corner_radius = 6 [project_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] text = "$text.1" @@ -235,7 +235,7 @@ line_number = "$text.2.color" line_number_active = "$text.0.color" selection = "$selection.host" guest_selections = "$selection.guests" -error_underline = "$status.bad" -warning_underline = "$status.warn" -info_underline = "$status.info" -hint_underline = "$status.info" +error_color = "$status.bad" +warning_color = "$status.warn" +info_color = "$status.info" +hint_color = "$status.info"