Fix layout of diagnostic blocks

This commit is contained in:
Max Brunsfeld 2023-12-19 15:41:00 -08:00
parent afd3bf7746
commit 3fccc648d6
2 changed files with 75 additions and 76 deletions

View file

@ -14,9 +14,9 @@ use editor::{
use futures::future::try_join_all; use futures::future::try_join_all;
use gpui::{ use gpui::{
actions, div, AnyElement, AnyView, AppContext, Context, Div, EventEmitter, FocusHandle, actions, div, AnyElement, AnyView, AppContext, Context, Div, EventEmitter, FocusHandle,
Focusable, FocusableView, InteractiveElement, IntoElement, Model, ParentElement, Render, Focusable, FocusableView, HighlightStyle, InteractiveElement, IntoElement, Model,
SharedString, Styled, Subscription, Task, View, ViewContext, VisualContext, WeakView, ParentElement, Render, SharedString, Styled, StyledText, Subscription, Task, View, ViewContext,
WindowContext, VisualContext, WeakView, WindowContext,
}; };
use language::{ use language::{
Anchor, Bias, Buffer, Diagnostic, DiagnosticEntry, DiagnosticSeverity, Point, Selection, Anchor, Bias, Buffer, Diagnostic, DiagnosticEntry, DiagnosticSeverity, Point, Selection,
@ -36,7 +36,7 @@ use std::{
}; };
use theme::ActiveTheme; use theme::ActiveTheme;
pub use toolbar_controls::ToolbarControls; pub use toolbar_controls::ToolbarControls;
use ui::{h_stack, prelude::*, HighlightedLabel, Icon, IconElement, Label}; use ui::{h_stack, prelude::*, Icon, IconElement, Label};
use util::TryFutureExt; use util::TryFutureExt;
use workspace::{ use workspace::{
item::{BreadcrumbText, Item, ItemEvent, ItemHandle}, item::{BreadcrumbText, Item, ItemEvent, ItemHandle},
@ -785,8 +785,10 @@ impl Item for ProjectDiagnosticsEditor {
} }
fn diagnostic_header_renderer(diagnostic: Diagnostic) -> RenderBlock { fn diagnostic_header_renderer(diagnostic: Diagnostic) -> RenderBlock {
let (message, highlights) = highlight_diagnostic_message(Vec::new(), &diagnostic.message); let (message, code_ranges) = highlight_diagnostic_message(&diagnostic);
Arc::new(move |_| { let message: SharedString = message.into();
Arc::new(move |cx| {
let highlight_style: HighlightStyle = cx.theme().colors().text_accent.into();
h_stack() h_stack()
.id("diagnostic header") .id("diagnostic header")
.py_2() .py_2()
@ -809,7 +811,14 @@ fn diagnostic_header_renderer(diagnostic: Diagnostic) -> RenderBlock {
.child( .child(
h_stack() h_stack()
.gap_1() .gap_1()
.child(HighlightedLabel::new(message.clone(), highlights.clone())) .child(
StyledText::new(message.clone()).with_highlights(
&cx.text_style(),
code_ranges
.iter()
.map(|range| (range.clone(), highlight_style)),
),
)
.when_some(diagnostic.code.as_ref(), |stack, code| { .when_some(diagnostic.code.as_ref(), |stack, code| {
stack.child(Label::new(format!("({code})")).color(Color::Muted)) stack.child(Label::new(format!("({code})")).color(Color::Muted))
}), }),

View file

@ -9719,90 +9719,80 @@ impl InvalidationRegion for SnippetState {
} }
} }
// impl Deref for EditorStyle {
// type Target = theme::Editor;
// fn deref(&self) -> &Self::Target {
// &self.theme
// }
// }
pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock { pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
let mut highlighted_lines = Vec::new(); let (text_without_backticks, code_ranges) = highlight_diagnostic_message(&diagnostic);
for (index, line) in diagnostic.message.lines().enumerate() {
let line = match &diagnostic.source {
Some(source) if index == 0 => {
let source_highlight = Vec::from_iter(0..source.len());
highlight_diagnostic_message(source_highlight, &format!("{source}: {line}"))
}
_ => highlight_diagnostic_message(Vec::new(), line),
};
highlighted_lines.push(line);
}
Arc::new(move |cx: &mut BlockContext| { Arc::new(move |cx: &mut BlockContext| {
let copy_id: SharedString = format!("copy-{}", cx.block_id.clone()).to_string().into(); let color = Some(cx.theme().colors().text_accent);
let group_id: SharedString = cx.block_id.to_string().into();
// TODO: Nate: We should tint the background of the block with the severity color // TODO: Nate: We should tint the background of the block with the severity color
// We need to extend the theme before we can do this // We need to extend the theme before we can do this
v_stack() h_stack()
.id(cx.block_id) .id(cx.block_id)
.group(group_id.clone())
.relative() .relative()
.pl(cx.anchor_x)
.size_full() .size_full()
.bg(gpui::red()) .gap_2()
.children(highlighted_lines.iter().map(|(line, highlights)| { .child(
let group_id = cx.block_id.to_string(); StyledText::new(text_without_backticks.clone()).with_highlights(
h_stack() &cx.text_style(),
.group(group_id.clone()) code_ranges.iter().map(|range| {
.gap_2() (
.absolute() range.clone(),
.left(cx.anchor_x) HighlightStyle {
.px_1p5() color,
.child(HighlightedLabel::new(line.clone(), highlights.clone())) ..Default::default()
.child( },
div().z_index(1).child( )
IconButton::new(copy_id.clone(), Icon::Copy) }),
.icon_color(Color::Muted) ),
.size(ButtonSize::Compact) )
.style(ButtonStyle::Transparent) .child(
.visible_on_hover(group_id) IconButton::new(("copy-block", cx.block_id), Icon::Copy)
.on_click(cx.listener({ .icon_color(Color::Muted)
let message = diagnostic.message.clone(); .size(ButtonSize::Compact)
move |_, _, cx| { .style(ButtonStyle::Transparent)
cx.write_to_clipboard(ClipboardItem::new(message.clone())) .visible_on_hover(group_id)
} .on_click(cx.listener({
})) let message = diagnostic.message.clone();
.tooltip(|cx| Tooltip::text("Copy diagnostic message", cx)), move |_, _, cx| cx.write_to_clipboard(ClipboardItem::new(message.clone()))
), }))
) .tooltip(|cx| Tooltip::text("Copy diagnostic message", cx)),
})) )
.into_any_element() .into_any_element()
}) })
} }
pub fn highlight_diagnostic_message( pub fn highlight_diagnostic_message(diagnostic: &Diagnostic) -> (SharedString, Vec<Range<usize>>) {
initial_highlights: Vec<usize>, let mut text_without_backticks = String::new();
message: &str, let mut code_ranges = Vec::new();
) -> (String, Vec<usize>) {
let mut message_without_backticks = String::new();
let mut prev_offset = 0;
let mut inside_block = false;
let mut highlights = initial_highlights;
for (match_ix, (offset, _)) in message
.match_indices('`')
.chain([(message.len(), "")])
.enumerate()
{
message_without_backticks.push_str(&message[prev_offset..offset]);
if inside_block {
highlights.extend(prev_offset - match_ix..offset - match_ix);
}
inside_block = !inside_block; if let Some(source) = &diagnostic.source {
prev_offset = offset + 1; text_without_backticks.push_str(&source);
code_ranges.push(0..source.len());
text_without_backticks.push_str(": ");
} }
(message_without_backticks, highlights) let mut prev_offset = 0;
let mut in_code_block = false;
for (ix, _) in diagnostic
.message
.match_indices('`')
.chain([(diagnostic.message.len(), "")])
{
let prev_len = text_without_backticks.len();
text_without_backticks.push_str(&diagnostic.message[prev_offset..ix]);
prev_offset = ix + 1;
if in_code_block {
code_ranges.push(prev_len..text_without_backticks.len());
in_code_block = false;
} else {
in_code_block = true;
}
}
(text_without_backticks.into(), code_ranges)
} }
pub fn diagnostic_style( pub fn diagnostic_style(