From c539069cbb9043df27538fc2f5bd5591b248508f Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 26 Oct 2021 17:57:50 -0700 Subject: [PATCH] Include diagnostic info in HighlightedChunks iterator Co-Authored-By: Nathan Sobo --- crates/editor/src/display_map.rs | 10 +- crates/editor/src/display_map/fold_map.rs | 31 +++-- crates/editor/src/display_map/tab_map.rs | 40 +++--- crates/editor/src/display_map/wrap_map.rs | 34 ++--- crates/editor/src/element.rs | 27 +++- crates/editor/src/lib.rs | 4 + crates/language/src/lib.rs | 161 +++++++++++++++++----- crates/language/src/tests.rs | 7 +- crates/theme/src/lib.rs | 10 ++ crates/zed/assets/themes/black.toml | 4 + crates/zed/assets/themes/dark.toml | 4 + crates/zed/assets/themes/light.toml | 6 +- 12 files changed, 248 insertions(+), 90 deletions(-) diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index a87f113743..596dc9507f 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -972,16 +972,16 @@ mod tests { ) -> Vec<(String, Option<&'a str>)> { let mut snapshot = map.update(cx, |map, cx| map.snapshot(cx)); let mut chunks: Vec<(String, Option<&str>)> = Vec::new(); - for (chunk, style_id) in snapshot.highlighted_chunks_for_rows(rows) { - let style_name = style_id.name(theme); + for chunk in snapshot.highlighted_chunks_for_rows(rows) { + let style_name = chunk.highlight_id.name(theme); if let Some((last_chunk, last_style_name)) = chunks.last_mut() { if style_name == *last_style_name { - last_chunk.push_str(chunk); + last_chunk.push_str(chunk.text); } else { - chunks.push((chunk.to_string(), style_name)); + chunks.push((chunk.text.to_string(), style_name)); } } else { - chunks.push((chunk.to_string(), style_name)); + chunks.push((chunk.text.to_string(), style_name)); } } chunks diff --git a/crates/editor/src/display_map/fold_map.rs b/crates/editor/src/display_map/fold_map.rs index 7a230aa165..efccbbbf5d 100644 --- a/crates/editor/src/display_map/fold_map.rs +++ b/crates/editor/src/display_map/fold_map.rs @@ -1,5 +1,7 @@ use gpui::{AppContext, ModelHandle}; -use language::{Anchor, AnchorRangeExt, Buffer, HighlightId, Point, TextSummary, ToOffset}; +use language::{ + Anchor, AnchorRangeExt, Buffer, HighlightId, HighlightedChunk, Point, TextSummary, ToOffset, +}; use parking_lot::Mutex; use std::{ cmp::{self, Ordering}, @@ -995,12 +997,12 @@ impl<'a> Iterator for Chunks<'a> { pub struct HighlightedChunks<'a> { transform_cursor: Cursor<'a, Transform, (FoldOffset, usize)>, buffer_chunks: language::HighlightedChunks<'a>, - buffer_chunk: Option<(usize, &'a str, HighlightId)>, + buffer_chunk: Option<(usize, HighlightedChunk<'a>)>, buffer_offset: usize, } impl<'a> Iterator for HighlightedChunks<'a> { - type Item = (&'a str, HighlightId); + type Item = HighlightedChunk<'a>; fn next(&mut self) -> Option { let transform = if let Some(item) = self.transform_cursor.item() { @@ -1022,34 +1024,35 @@ impl<'a> Iterator for HighlightedChunks<'a> { self.transform_cursor.next(&()); } - return Some((output_text, HighlightId::default())); + return Some(HighlightedChunk { + text: output_text, + highlight_id: HighlightId::default(), + diagnostic: None, + }); } // Retrieve a chunk from the current location in the buffer. if self.buffer_chunk.is_none() { let chunk_offset = self.buffer_chunks.offset(); - self.buffer_chunk = self - .buffer_chunks - .next() - .map(|(chunk, capture_ix)| (chunk_offset, chunk, capture_ix)); + self.buffer_chunk = self.buffer_chunks.next().map(|chunk| (chunk_offset, chunk)); } // Otherwise, take a chunk from the buffer's text. - if let Some((chunk_offset, mut chunk, capture_ix)) = self.buffer_chunk { + if let Some((chunk_offset, mut chunk)) = self.buffer_chunk { let offset_in_chunk = self.buffer_offset - chunk_offset; - chunk = &chunk[offset_in_chunk..]; + chunk.text = &chunk.text[offset_in_chunk..]; // Truncate the chunk so that it ends at the next fold. let region_end = self.transform_cursor.end(&()).1 - self.buffer_offset; - if chunk.len() >= region_end { - chunk = &chunk[0..region_end]; + if chunk.text.len() >= region_end { + chunk.text = &chunk.text[0..region_end]; self.transform_cursor.next(&()); } else { self.buffer_chunk.take(); } - self.buffer_offset += chunk.len(); - return Some((chunk, capture_ix)); + self.buffer_offset += chunk.text.len(); + return Some(chunk); } None diff --git a/crates/editor/src/display_map/tab_map.rs b/crates/editor/src/display_map/tab_map.rs index cfab4fd941..93fae6d6b2 100644 --- a/crates/editor/src/display_map/tab_map.rs +++ b/crates/editor/src/display_map/tab_map.rs @@ -1,5 +1,5 @@ use super::fold_map::{self, FoldEdit, FoldPoint, Snapshot as FoldSnapshot}; -use language::{rope, HighlightId}; +use language::{rope, HighlightedChunk}; use parking_lot::Mutex; use std::{mem, ops::Range}; use sum_tree::Bias; @@ -173,9 +173,11 @@ impl Snapshot { .highlighted_chunks(input_start..input_end), column: expanded_char_column, tab_size: self.tab_size, - chunk: &SPACES[0..to_next_stop], + chunk: HighlightedChunk { + text: &SPACES[0..to_next_stop], + ..Default::default() + }, skip_leading_tab: to_next_stop > 0, - style_id: Default::default(), } } @@ -415,23 +417,21 @@ impl<'a> Iterator for Chunks<'a> { pub struct HighlightedChunks<'a> { fold_chunks: fold_map::HighlightedChunks<'a>, - chunk: &'a str, - style_id: HighlightId, + chunk: HighlightedChunk<'a>, column: usize, tab_size: usize, skip_leading_tab: bool, } impl<'a> Iterator for HighlightedChunks<'a> { - type Item = (&'a str, HighlightId); + type Item = HighlightedChunk<'a>; fn next(&mut self) -> Option { - if self.chunk.is_empty() { - if let Some((chunk, style_id)) = self.fold_chunks.next() { + if self.chunk.text.is_empty() { + if let Some(chunk) = self.fold_chunks.next() { self.chunk = chunk; - self.style_id = style_id; if self.skip_leading_tab { - self.chunk = &self.chunk[1..]; + self.chunk.text = &self.chunk.text[1..]; self.skip_leading_tab = false; } } else { @@ -439,18 +439,24 @@ impl<'a> Iterator for HighlightedChunks<'a> { } } - for (ix, c) in self.chunk.char_indices() { + for (ix, c) in self.chunk.text.char_indices() { match c { '\t' => { if ix > 0 { - let (prefix, suffix) = self.chunk.split_at(ix); - self.chunk = suffix; - return Some((prefix, self.style_id)); + let (prefix, suffix) = self.chunk.text.split_at(ix); + self.chunk.text = suffix; + return Some(HighlightedChunk { + text: prefix, + ..self.chunk + }); } else { - self.chunk = &self.chunk[1..]; + self.chunk.text = &self.chunk.text[1..]; let len = self.tab_size - self.column % self.tab_size; self.column += len; - return Some((&SPACES[0..len], self.style_id)); + return Some(HighlightedChunk { + text: &SPACES[0..len], + ..self.chunk + }); } } '\n' => self.column = 0, @@ -458,7 +464,7 @@ impl<'a> Iterator for HighlightedChunks<'a> { } } - Some((mem::take(&mut self.chunk), mem::take(&mut self.style_id))) + Some(mem::take(&mut self.chunk)) } } diff --git a/crates/editor/src/display_map/wrap_map.rs b/crates/editor/src/display_map/wrap_map.rs index 897dfa01b9..a62c67dbce 100644 --- a/crates/editor/src/display_map/wrap_map.rs +++ b/crates/editor/src/display_map/wrap_map.rs @@ -3,7 +3,7 @@ use super::{ tab_map::{self, Edit as TabEdit, Snapshot as TabSnapshot, TabPoint, TextSummary}, }; use gpui::{fonts::FontId, text_layout::LineWrapper, Entity, ModelContext, Task}; -use language::{HighlightId, Point}; +use language::{HighlightedChunk, Point}; use lazy_static::lazy_static; use smol::future::yield_now; use std::{collections::VecDeque, ops::Range, time::Duration}; @@ -52,8 +52,7 @@ pub struct Chunks<'a> { pub struct HighlightedChunks<'a> { input_chunks: tab_map::HighlightedChunks<'a>, - input_chunk: &'a str, - style_id: HighlightId, + input_chunk: HighlightedChunk<'a>, output_position: WrapPoint, max_output_row: u32, transforms: Cursor<'a, Transform, (WrapPoint, TabPoint)>, @@ -490,8 +489,7 @@ impl Snapshot { .min(self.tab_snapshot.max_point()); HighlightedChunks { input_chunks: self.tab_snapshot.highlighted_chunks(input_start..input_end), - input_chunk: "", - style_id: HighlightId::default(), + input_chunk: Default::default(), output_position: output_start, max_output_row: rows.end, transforms, @@ -674,7 +672,7 @@ impl<'a> Iterator for Chunks<'a> { } impl<'a> Iterator for HighlightedChunks<'a> { - type Item = (&'a str, HighlightId); + type Item = HighlightedChunk<'a>; fn next(&mut self) -> Option { if self.output_position.row() >= self.max_output_row { @@ -699,18 +697,19 @@ impl<'a> Iterator for HighlightedChunks<'a> { self.output_position.0 += summary; self.transforms.next(&()); - return Some((&display_text[start_ix..end_ix], self.style_id)); + return Some(HighlightedChunk { + text: &display_text[start_ix..end_ix], + ..self.input_chunk + }); } - if self.input_chunk.is_empty() { - let (chunk, style_id) = self.input_chunks.next().unwrap(); - self.input_chunk = chunk; - self.style_id = style_id; + if self.input_chunk.text.is_empty() { + self.input_chunk = self.input_chunks.next().unwrap(); } let mut input_len = 0; let transform_end = self.transforms.end(&()).0; - for c in self.input_chunk.chars() { + for c in self.input_chunk.text.chars() { let char_len = c.len_utf8(); input_len += char_len; if c == '\n' { @@ -726,9 +725,12 @@ impl<'a> Iterator for HighlightedChunks<'a> { } } - let (prefix, suffix) = self.input_chunk.split_at(input_len); - self.input_chunk = suffix; - Some((prefix, self.style_id)) + let (prefix, suffix) = self.input_chunk.text.split_at(input_len); + self.input_chunk.text = suffix; + Some(HighlightedChunk { + text: prefix, + ..self.input_chunk + }) } } @@ -1090,7 +1092,7 @@ mod tests { let actual_text = self .highlighted_chunks_for_rows(start_row..end_row) - .map(|c| c.0) + .map(|c| c.text) .collect::(); assert_eq!( expected_text, diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index b18e72b2d7..f538f3f4cb 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::HighlightId; +use language::{DiagnosticSeverity, HighlightedChunk}; use smallvec::SmallVec; use std::{ cmp::{self, Ordering}, @@ -495,8 +495,12 @@ impl EditorElement { let mut line_exceeded_max_len = false; let chunks = snapshot.highlighted_chunks_for_rows(rows.clone()); - 'outer: for (chunk, style_ix) in chunks.chain(Some(("\n", HighlightId::default()))) { - for (ix, mut line_chunk) in chunk.split('\n').enumerate() { + let newline_chunk = HighlightedChunk { + text: "\n", + ..Default::default() + }; + 'outer: for chunk in chunks.chain([newline_chunk]) { + for (ix, mut line_chunk) in chunk.text.split('\n').enumerate() { if ix > 0 { layouts.push(cx.text_layout_cache.layout_str( &line, @@ -513,7 +517,8 @@ impl EditorElement { } if !line_chunk.is_empty() && !line_exceeded_max_len { - let highlight_style = style_ix + let highlight_style = chunk + .highlight_id .style(&style.syntax) .unwrap_or(style.text.clone().into()); // Avoid a lookup if the font properties match the previous ones. @@ -537,13 +542,25 @@ impl EditorElement { line_exceeded_max_len = true; } + 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, + } + } else { + highlight_style.underline + }; + line.push_str(line_chunk); styles.push(( line_chunk.len(), RunStyle { font_id, color: highlight_style.color, - underline: highlight_style.underline, + underline, }, )); prev_font_id = font_id; diff --git a/crates/editor/src/lib.rs b/crates/editor/src/lib.rs index 05eb9fa7e5..fc28e04729 100644 --- a/crates/editor/src/lib.rs +++ b/crates/editor/src/lib.rs @@ -2774,6 +2774,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(), } }, } diff --git a/crates/language/src/lib.rs b/crates/language/src/lib.rs index 86714def0f..056661bb3f 100644 --- a/crates/language/src/lib.rs +++ b/crates/language/src/lib.rs @@ -13,7 +13,7 @@ use clock::ReplicaId; use futures::FutureExt as _; use gpui::{AppContext, Entity, ModelContext, MutableAppContext, Task}; use lazy_static::lazy_static; -use lsp::{DiagnosticSeverity, LanguageServer}; +use lsp::LanguageServer; use parking_lot::Mutex; use postage::{prelude::Stream, sink::Sink, watch}; use rpc::proto; @@ -26,16 +26,19 @@ use std::{ collections::{BTreeMap, HashMap, HashSet}, ffi::OsString, future::Future, - iter::Iterator, + iter::{Iterator, Peekable}, ops::{Deref, DerefMut, Range}, path::{Path, PathBuf}, str, sync::Arc, time::{Duration, Instant, SystemTime, UNIX_EPOCH}, + vec, }; use tree_sitter::{InputEdit, Parser, QueryCursor, Tree}; use util::{post_inc, TryFutureExt as _}; +pub use lsp::DiagnosticSeverity; + thread_local! { static PARSER: RefCell = RefCell::new(Parser::new()); } @@ -68,6 +71,7 @@ pub struct Buffer { pub struct Snapshot { text: buffer::Snapshot, tree: Option, + diagnostics: AnchorRangeMultimap<(DiagnosticSeverity, String)>, is_parsing: bool, language: Option>, query_cursor: QueryCursorHandle, @@ -182,15 +186,34 @@ struct Highlights<'a> { pub struct HighlightedChunks<'a> { range: Range, chunks: Chunks<'a>, + diagnostic_endpoints: Peekable>, + error_depth: usize, + warning_depth: usize, + information_depth: usize, + hint_depth: usize, highlights: Option>, } +#[derive(Clone, Copy, Debug, Default)] +pub struct HighlightedChunk<'a> { + pub text: &'a str, + pub highlight_id: HighlightId, + pub diagnostic: Option, +} + struct Diff { base_version: clock::Global, new_text: Arc, changes: Vec<(ChangeTag, usize)>, } +#[derive(Clone, Copy)] +struct DiagnosticEndpoint { + offset: usize, + is_start: bool, + severity: DiagnosticSeverity, +} + impl Buffer { pub fn new>>( replica_id: ReplicaId, @@ -275,6 +298,7 @@ impl Buffer { Snapshot { text: self.text.snapshot(), tree: self.syntax_tree(), + diagnostics: self.diagnostics.clone(), is_parsing: self.parsing_in_background, language: self.language.clone(), query_cursor: QueryCursorHandle::new(), @@ -673,7 +697,7 @@ impl Buffer { let content = self.content(); let range = range.start.to_offset(&content)..range.end.to_offset(&content); self.diagnostics - .intersecting_point_ranges(range, content, true) + .intersecting_ranges(range, content, true) .map(move |(_, range, (severity, message))| Diagnostic { range, severity: *severity, @@ -1021,7 +1045,9 @@ impl Buffer { let abs_path = self .file .as_ref() - .map_or(PathBuf::new(), |file| file.abs_path(cx).unwrap()); + .map_or(Path::new("/").to_path_buf(), |file| { + file.abs_path(cx).unwrap() + }); let version = post_inc(&mut language_server.next_version); let snapshot = LanguageServerSnapshot { @@ -1462,30 +1488,54 @@ impl Snapshot { range: Range, ) -> HighlightedChunks { let range = range.start.to_offset(&*self)..range.end.to_offset(&*self); - let chunks = self.text.as_rope().chunks_in_range(range.clone()); - if let Some((language, tree)) = self.language.as_ref().zip(self.tree.as_ref()) { - let captures = self.query_cursor.set_byte_range(range.clone()).captures( - &language.highlights_query, - tree.root_node(), - TextProvider(self.text.as_rope()), - ); - HighlightedChunks { - range, - chunks, - highlights: Some(Highlights { + let mut diagnostic_endpoints = Vec::::new(); + for (_, range, (severity, _)) in + self.diagnostics + .intersecting_ranges(range.clone(), self.content(), true) + { + diagnostic_endpoints.push(DiagnosticEndpoint { + offset: range.start, + is_start: true, + severity: *severity, + }); + diagnostic_endpoints.push(DiagnosticEndpoint { + offset: range.end, + is_start: false, + severity: *severity, + }); + } + diagnostic_endpoints.sort_unstable_by_key(|endpoint| endpoint.offset); + let diagnostic_endpoints = diagnostic_endpoints.into_iter().peekable(); + + let chunks = self.text.as_rope().chunks_in_range(range.clone()); + let highlights = + if let Some((language, tree)) = self.language.as_ref().zip(self.tree.as_ref()) { + let captures = self.query_cursor.set_byte_range(range.clone()).captures( + &language.highlights_query, + tree.root_node(), + TextProvider(self.text.as_rope()), + ); + + Some(Highlights { captures, next_capture: None, stack: Default::default(), highlight_map: language.highlight_map(), - }), - } - } else { - HighlightedChunks { - range, - chunks, - highlights: None, - } + }) + } else { + None + }; + + HighlightedChunks { + range, + chunks, + diagnostic_endpoints, + error_depth: 0, + warning_depth: 0, + information_depth: 0, + hint_depth: 0, + highlights, } } } @@ -1495,6 +1545,7 @@ impl Clone for Snapshot { Self { text: self.text.clone(), tree: self.tree.clone(), + diagnostics: self.diagnostics.clone(), is_parsing: self.is_parsing, language: self.language.clone(), query_cursor: QueryCursorHandle::new(), @@ -1556,13 +1607,43 @@ impl<'a> HighlightedChunks<'a> { pub fn offset(&self) -> usize { self.range.start } + + fn update_diagnostic_depths(&mut self, endpoint: DiagnosticEndpoint) { + let depth = match endpoint.severity { + DiagnosticSeverity::ERROR => &mut self.error_depth, + DiagnosticSeverity::WARNING => &mut self.warning_depth, + DiagnosticSeverity::INFORMATION => &mut self.information_depth, + DiagnosticSeverity::HINT => &mut self.hint_depth, + _ => return, + }; + if endpoint.is_start { + *depth += 1; + } else { + *depth -= 1; + } + } + + fn current_diagnostic_severity(&mut self) -> Option { + if self.error_depth > 0 { + Some(DiagnosticSeverity::ERROR) + } else if self.warning_depth > 0 { + Some(DiagnosticSeverity::WARNING) + } else if self.information_depth > 0 { + Some(DiagnosticSeverity::INFORMATION) + } else if self.hint_depth > 0 { + Some(DiagnosticSeverity::HINT) + } else { + None + } + } } impl<'a> Iterator for HighlightedChunks<'a> { - type Item = (&'a str, HighlightId); + type Item = HighlightedChunk<'a>; fn next(&mut self) -> Option { let mut next_capture_start = usize::MAX; + let mut next_diagnostic_endpoint = usize::MAX; if let Some(highlights) = self.highlights.as_mut() { while let Some((parent_capture_end, _)) = highlights.stack.last() { @@ -1583,22 +1664,36 @@ impl<'a> Iterator for HighlightedChunks<'a> { next_capture_start = capture.node.start_byte(); break; } else { - let style_id = highlights.highlight_map.get(capture.index); - highlights.stack.push((capture.node.end_byte(), style_id)); + let highlight_id = highlights.highlight_map.get(capture.index); + highlights + .stack + .push((capture.node.end_byte(), highlight_id)); highlights.next_capture = highlights.captures.next(); } } } + while let Some(endpoint) = self.diagnostic_endpoints.peek().copied() { + if endpoint.offset <= self.range.start { + self.update_diagnostic_depths(endpoint); + self.diagnostic_endpoints.next(); + } else { + next_diagnostic_endpoint = endpoint.offset; + break; + } + } + if let Some(chunk) = self.chunks.peek() { let chunk_start = self.range.start; - let mut chunk_end = (self.chunks.offset() + chunk.len()).min(next_capture_start); - let mut style_id = HighlightId::default(); - if let Some((parent_capture_end, parent_style_id)) = + let mut chunk_end = (self.chunks.offset() + chunk.len()) + .min(next_capture_start) + .min(next_diagnostic_endpoint); + let mut highlight_id = HighlightId::default(); + if let Some((parent_capture_end, parent_highlight_id)) = self.highlights.as_ref().and_then(|h| h.stack.last()) { chunk_end = chunk_end.min(*parent_capture_end); - style_id = *parent_style_id; + highlight_id = *parent_highlight_id; } let slice = @@ -1608,7 +1703,11 @@ impl<'a> Iterator for HighlightedChunks<'a> { self.chunks.next().unwrap(); } - Some((slice, style_id)) + Some(HighlightedChunk { + text: slice, + highlight_id, + diagnostic: self.current_diagnostic_severity(), + }) } else { None } diff --git a/crates/language/src/tests.rs b/crates/language/src/tests.rs index 988b1d0c05..1dbf9700ee 100644 --- a/crates/language/src/tests.rs +++ b/crates/language/src/tests.rs @@ -475,7 +475,12 @@ async fn test_diagnostics(mut cx: gpui::TestAppContext) { message: "undefined variable 'CCC'".to_string() } ] - ) + ); + + dbg!(buffer + .snapshot() + .highlighted_text_for_range(0..buffer.len()) + .collect::>()); }); } diff --git a/crates/theme/src/lib.rs b/crates/theme/src/lib.rs index 46b3f4a750..2a0abc4395 100644 --- a/crates/theme/src/lib.rs +++ b/crates/theme/src/lib.rs @@ -214,6 +214,12 @@ pub struct EditorStyle { pub line_number_active: Color, pub guest_selections: Vec, pub syntax: Arc, + pub error_underline: Color, + pub warning_underline: Color, + #[serde(default)] + pub information_underline: Color, + #[serde(default)] + pub hint_underline: Color, } #[derive(Clone, Copy, Default, Deserialize)] @@ -254,6 +260,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(), } } } diff --git a/crates/zed/assets/themes/black.toml b/crates/zed/assets/themes/black.toml index d37b7905be..e99629c5ce 100644 --- a/crates/zed/assets/themes/black.toml +++ b/crates/zed/assets/themes/black.toml @@ -39,6 +39,10 @@ bad = "#b7372e" active_line = "#00000033" hover = "#00000033" +[editor] +error_underline = "#ff0000" +warning_underline = "#00ffff" + [editor.syntax] keyword = { color = "#0086c0", weight = "bold" } function = "#dcdcaa" diff --git a/crates/zed/assets/themes/dark.toml b/crates/zed/assets/themes/dark.toml index 694e346911..ce64e3c3f0 100644 --- a/crates/zed/assets/themes/dark.toml +++ b/crates/zed/assets/themes/dark.toml @@ -39,6 +39,10 @@ bad = "#b7372e" active_line = "#00000022" hover = "#00000033" +[editor] +error_underline = "#ff0000" +warning_underline = "#00ffff" + [editor.syntax] keyword = { color = "#0086c0", weight = "bold" } function = "#dcdcaa" diff --git a/crates/zed/assets/themes/light.toml b/crates/zed/assets/themes/light.toml index 677a9fd6f6..13803c11a8 100644 --- a/crates/zed/assets/themes/light.toml +++ b/crates/zed/assets/themes/light.toml @@ -26,7 +26,7 @@ guests = [ { selection = "#EE823133", cursor = "#EE8231" }, { selection = "#5A2B9233", cursor = "#5A2B92" }, { selection = "#FDF35133", cursor = "#FDF351" }, - { selection = "#4EACAD33", cursor = "#4EACAD" } + { selection = "#4EACAD33", cursor = "#4EACAD" }, ] [status] @@ -39,6 +39,10 @@ bad = "#b7372e" active_line = "#00000008" hover = "#0000000D" +[editor] +error_underline = "#ff0000" +warning_underline = "#00ffff" + [editor.syntax] keyword = { color = "#0000fa", weight = "bold" } function = "#795e26"