Color diagnostic messages based on their severity

Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
Antonio Scandurra 2021-11-18 17:47:10 +01:00
parent f39942863b
commit 1a8b23e118
6 changed files with 103 additions and 58 deletions

View File

@ -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<Item = &str> {
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<u32>,
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<Item = char> + 'a {
@ -1025,7 +1026,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)) {
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 {

View File

@ -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<Arc<dyn Fn(&AppContext) -> 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<Arc<dyn Fn(&AppContext) -> Vec<(usize, HighlightStyle)>>>,
pub disposition: BlockDisposition,
}
@ -92,11 +92,12 @@ pub struct Chunks<'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<slice::Iter<'a, (usize, HighlightStyle)>>,
runs: iter::Peekable<vec::IntoIter<(usize, HighlightStyle)>>,
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<u32>, theme: Option<&'a SyntaxTheme>) -> Chunks<'a> {
pub fn chunks<'a>(
&'a self,
rows: Range<u32>,
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<u32>) -> Self {
fn new(block: &'a Block, 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.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 1",
disposition: BlockDisposition::Above,
runs: vec![],
build_runs: None,
},
BlockProperties {
position: Point::new(1, 1),
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::<String>();
assert_eq!(

View File

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

View File

@ -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::<Point>(group_id)
.map(|(range, diagnostic)| (range, diagnostic.message.clone()))
.map(|(range, diagnostic)| (range, diagnostic.clone()))
.collect::<Vec<_>>();
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<u32>,
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<Point> {
}
}
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::*;

View File

@ -227,12 +227,12 @@ pub struct EditorStyle {
pub line_number_active: Color,
pub guest_selections: Vec<SelectionStyle>,
pub syntax: Arc<SyntaxTheme>,
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(),
}
}
}

View File

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