Add an experimental, WIP diagnostics grouping panel (#14515)
Provide a current, broken state as an experimental way to browse diagnostics. The diagnostics are grouped by lines and reduced into a block that, in case of multiple diagnostics per line, could be toggled back and forth to show more diagnostics on the line. Use `grouped_diagnostics::Deploy` to show the panel. Issues remaining: * panic on warnings toggle due to incorrect excerpt manipulation * badly styled blocks * no key bindings to navigate between blocks and toggle them * overall odd usability gains for certain groups of people Due to all above, the thing is feature-gated and not exposed to regular people. Release Notes: - N/A
This commit is contained in:
parent
2c6cb4ec16
commit
d7a25c1696
13 changed files with 1647 additions and 95 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -3402,11 +3402,13 @@ dependencies = [
|
||||||
"ctor",
|
"ctor",
|
||||||
"editor",
|
"editor",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
|
"feature_flags",
|
||||||
"futures 0.3.28",
|
"futures 0.3.28",
|
||||||
"gpui",
|
"gpui",
|
||||||
"language",
|
"language",
|
||||||
"log",
|
"log",
|
||||||
"lsp",
|
"lsp",
|
||||||
|
"multi_buffer",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"project",
|
"project",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
|
|
|
@ -2549,7 +2549,10 @@ fn render_slash_command_output_toggle(
|
||||||
fold: ToggleFold,
|
fold: ToggleFold,
|
||||||
_cx: &mut WindowContext,
|
_cx: &mut WindowContext,
|
||||||
) -> AnyElement {
|
) -> AnyElement {
|
||||||
Disclosure::new(("slash-command-output-fold-indicator", row.0), !is_folded)
|
Disclosure::new(
|
||||||
|
("slash-command-output-fold-indicator", row.0 as u64),
|
||||||
|
!is_folded,
|
||||||
|
)
|
||||||
.selected(is_folded)
|
.selected(is_folded)
|
||||||
.on_click(move |_e, cx| fold(!is_folded, cx))
|
.on_click(move |_e, cx| fold(!is_folded, cx))
|
||||||
.into_any_element()
|
.into_any_element()
|
||||||
|
|
|
@ -18,11 +18,13 @@ collections.workspace = true
|
||||||
ctor.workspace = true
|
ctor.workspace = true
|
||||||
editor.workspace = true
|
editor.workspace = true
|
||||||
env_logger.workspace = true
|
env_logger.workspace = true
|
||||||
|
feature_flags.workspace = true
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
gpui.workspace = true
|
gpui.workspace = true
|
||||||
language.workspace = true
|
language.workspace = true
|
||||||
log.workspace = true
|
log.workspace = true
|
||||||
lsp.workspace = true
|
lsp.workspace = true
|
||||||
|
multi_buffer.workspace = true
|
||||||
project.workspace = true
|
project.workspace = true
|
||||||
rand.workspace = true
|
rand.workspace = true
|
||||||
schemars.workspace = true
|
schemars.workspace = true
|
||||||
|
|
|
@ -4,6 +4,7 @@ mod toolbar_controls;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod diagnostics_tests;
|
mod diagnostics_tests;
|
||||||
|
mod grouped_diagnostics;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use collections::{BTreeSet, HashSet};
|
use collections::{BTreeSet, HashSet};
|
||||||
|
@ -14,6 +15,7 @@ use editor::{
|
||||||
scroll::Autoscroll,
|
scroll::Autoscroll,
|
||||||
Editor, EditorEvent, ExcerptId, ExcerptRange, MultiBuffer, ToOffset,
|
Editor, EditorEvent, ExcerptId, ExcerptRange, MultiBuffer, ToOffset,
|
||||||
};
|
};
|
||||||
|
use feature_flags::FeatureFlagAppExt;
|
||||||
use futures::{
|
use futures::{
|
||||||
channel::mpsc::{self, UnboundedSender},
|
channel::mpsc::{self, UnboundedSender},
|
||||||
StreamExt as _,
|
StreamExt as _,
|
||||||
|
@ -52,6 +54,9 @@ pub fn init(cx: &mut AppContext) {
|
||||||
ProjectDiagnosticsSettings::register(cx);
|
ProjectDiagnosticsSettings::register(cx);
|
||||||
cx.observe_new_views(ProjectDiagnosticsEditor::register)
|
cx.observe_new_views(ProjectDiagnosticsEditor::register)
|
||||||
.detach();
|
.detach();
|
||||||
|
if !cx.has_flag::<feature_flags::GroupedDiagnostics>() {
|
||||||
|
grouped_diagnostics::init(cx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ProjectDiagnosticsEditor {
|
struct ProjectDiagnosticsEditor {
|
||||||
|
@ -466,7 +471,9 @@ impl ProjectDiagnosticsEditor {
|
||||||
position: (excerpt_id, entry.range.start),
|
position: (excerpt_id, entry.range.start),
|
||||||
height: diagnostic.message.matches('\n').count() as u8 + 1,
|
height: diagnostic.message.matches('\n').count() as u8 + 1,
|
||||||
style: BlockStyle::Fixed,
|
style: BlockStyle::Fixed,
|
||||||
render: diagnostic_block_renderer(diagnostic, true),
|
render: diagnostic_block_renderer(
|
||||||
|
diagnostic, None, true, true,
|
||||||
|
),
|
||||||
disposition: BlockDisposition::Below,
|
disposition: BlockDisposition::Below,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -798,7 +805,7 @@ impl Item for ProjectDiagnosticsEditor {
|
||||||
const DIAGNOSTIC_HEADER: &'static str = "diagnostic header";
|
const DIAGNOSTIC_HEADER: &'static str = "diagnostic header";
|
||||||
|
|
||||||
fn diagnostic_header_renderer(diagnostic: Diagnostic) -> RenderBlock {
|
fn diagnostic_header_renderer(diagnostic: Diagnostic) -> RenderBlock {
|
||||||
let (message, code_ranges) = highlight_diagnostic_message(&diagnostic);
|
let (message, code_ranges) = highlight_diagnostic_message(&diagnostic, None);
|
||||||
let message: SharedString = message;
|
let message: SharedString = message;
|
||||||
Box::new(move |cx| {
|
Box::new(move |cx| {
|
||||||
let highlight_style: HighlightStyle = cx.theme().colors().text_accent.into();
|
let highlight_style: HighlightStyle = cx.theme().colors().text_accent.into();
|
||||||
|
|
|
@ -973,8 +973,8 @@ fn editor_blocks(
|
||||||
blocks.extend(
|
blocks.extend(
|
||||||
snapshot
|
snapshot
|
||||||
.blocks_in_range(DisplayRow(0)..snapshot.max_point().row())
|
.blocks_in_range(DisplayRow(0)..snapshot.max_point().row())
|
||||||
.enumerate()
|
.filter_map(|(row, block)| {
|
||||||
.filter_map(|(ix, (row, block))| {
|
let transform_block_id = block.id();
|
||||||
let name: SharedString = match block {
|
let name: SharedString = match block {
|
||||||
TransformBlock::Custom(block) => {
|
TransformBlock::Custom(block) => {
|
||||||
let mut element = block.render(&mut BlockContext {
|
let mut element = block.render(&mut BlockContext {
|
||||||
|
@ -984,7 +984,7 @@ fn editor_blocks(
|
||||||
line_height: px(0.),
|
line_height: px(0.),
|
||||||
em_width: px(0.),
|
em_width: px(0.),
|
||||||
max_width: px(0.),
|
max_width: px(0.),
|
||||||
block_id: ix,
|
transform_block_id,
|
||||||
editor_style: &editor::EditorStyle::default(),
|
editor_style: &editor::EditorStyle::default(),
|
||||||
});
|
});
|
||||||
let element = element.downcast_mut::<Stateful<Div>>().unwrap();
|
let element = element.downcast_mut::<Stateful<Div>>().unwrap();
|
||||||
|
|
1419
crates/diagnostics/src/grouped_diagnostics.rs
Normal file
1419
crates/diagnostics/src/grouped_diagnostics.rs
Normal file
File diff suppressed because it is too large
Load diff
|
@ -30,6 +30,7 @@ use crate::{
|
||||||
pub use block_map::{
|
pub use block_map::{
|
||||||
BlockBufferRows, BlockChunks as DisplayChunks, BlockContext, BlockDisposition, BlockId,
|
BlockBufferRows, BlockChunks as DisplayChunks, BlockContext, BlockDisposition, BlockId,
|
||||||
BlockMap, BlockPoint, BlockProperties, BlockStyle, RenderBlock, TransformBlock,
|
BlockMap, BlockPoint, BlockProperties, BlockStyle, RenderBlock, TransformBlock,
|
||||||
|
TransformBlockId,
|
||||||
};
|
};
|
||||||
use block_map::{BlockRow, BlockSnapshot};
|
use block_map::{BlockRow, BlockSnapshot};
|
||||||
use collections::{HashMap, HashSet};
|
use collections::{HashMap, HashSet};
|
||||||
|
|
|
@ -4,7 +4,7 @@ use super::{
|
||||||
};
|
};
|
||||||
use crate::{EditorStyle, GutterDimensions};
|
use crate::{EditorStyle, GutterDimensions};
|
||||||
use collections::{Bound, HashMap, HashSet};
|
use collections::{Bound, HashMap, HashSet};
|
||||||
use gpui::{AnyElement, Pixels, WindowContext};
|
use gpui::{AnyElement, EntityId, Pixels, WindowContext};
|
||||||
use language::{BufferSnapshot, Chunk, Patch, Point};
|
use language::{BufferSnapshot, Chunk, Patch, Point};
|
||||||
use multi_buffer::{Anchor, ExcerptId, ExcerptRange, MultiBufferRow, ToPoint as _};
|
use multi_buffer::{Anchor, ExcerptId, ExcerptRange, MultiBufferRow, ToPoint as _};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
@ -20,6 +20,7 @@ use std::{
|
||||||
};
|
};
|
||||||
use sum_tree::{Bias, SumTree};
|
use sum_tree::{Bias, SumTree};
|
||||||
use text::Edit;
|
use text::Edit;
|
||||||
|
use ui::ElementId;
|
||||||
|
|
||||||
const NEWLINES: &[u8] = &[b'\n'; u8::MAX as usize];
|
const NEWLINES: &[u8] = &[b'\n'; u8::MAX as usize];
|
||||||
|
|
||||||
|
@ -53,6 +54,12 @@ pub struct BlockSnapshot {
|
||||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct BlockId(usize);
|
pub struct BlockId(usize);
|
||||||
|
|
||||||
|
impl Into<ElementId> for BlockId {
|
||||||
|
fn into(self) -> ElementId {
|
||||||
|
ElementId::Integer(self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
|
#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
|
||||||
pub struct BlockPoint(pub Point);
|
pub struct BlockPoint(pub Point);
|
||||||
|
|
||||||
|
@ -62,7 +69,7 @@ pub struct BlockRow(pub(super) 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);
|
||||||
|
|
||||||
pub type RenderBlock = Box<dyn Send + Fn(&mut BlockContext) -> AnyElement>;
|
pub type RenderBlock = Box<dyn Send + FnMut(&mut BlockContext) -> AnyElement>;
|
||||||
|
|
||||||
pub struct Block {
|
pub struct Block {
|
||||||
id: BlockId,
|
id: BlockId,
|
||||||
|
@ -77,11 +84,22 @@ pub struct BlockProperties<P> {
|
||||||
pub position: P,
|
pub position: P,
|
||||||
pub height: u8,
|
pub height: u8,
|
||||||
pub style: BlockStyle,
|
pub style: BlockStyle,
|
||||||
pub render: Box<dyn Send + Fn(&mut BlockContext) -> AnyElement>,
|
pub render: RenderBlock,
|
||||||
pub disposition: BlockDisposition,
|
pub disposition: BlockDisposition,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
impl<P: Debug> Debug for BlockProperties<P> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("BlockProperties")
|
||||||
|
.field("position", &self.position)
|
||||||
|
.field("height", &self.height)
|
||||||
|
.field("style", &self.style)
|
||||||
|
.field("disposition", &self.disposition)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
||||||
pub enum BlockStyle {
|
pub enum BlockStyle {
|
||||||
Fixed,
|
Fixed,
|
||||||
Flex,
|
Flex,
|
||||||
|
@ -95,10 +113,47 @@ pub struct BlockContext<'a, 'b> {
|
||||||
pub gutter_dimensions: &'b GutterDimensions,
|
pub gutter_dimensions: &'b GutterDimensions,
|
||||||
pub em_width: Pixels,
|
pub em_width: Pixels,
|
||||||
pub line_height: Pixels,
|
pub line_height: Pixels,
|
||||||
pub block_id: usize,
|
pub transform_block_id: TransformBlockId,
|
||||||
pub editor_style: &'b EditorStyle,
|
pub editor_style: &'b EditorStyle,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub enum TransformBlockId {
|
||||||
|
Block(BlockId),
|
||||||
|
ExcerptHeader(ExcerptId),
|
||||||
|
ExcerptFooter(ExcerptId),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<TransformBlockId> for EntityId {
|
||||||
|
fn from(value: TransformBlockId) -> Self {
|
||||||
|
match value {
|
||||||
|
TransformBlockId::Block(BlockId(id)) => EntityId::from(id as u64),
|
||||||
|
TransformBlockId::ExcerptHeader(id) => id.into(),
|
||||||
|
TransformBlockId::ExcerptFooter(id) => id.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<ElementId> for TransformBlockId {
|
||||||
|
fn into(self) -> ElementId {
|
||||||
|
match self {
|
||||||
|
Self::Block(BlockId(id)) => ("Block", id).into(),
|
||||||
|
Self::ExcerptHeader(id) => ("ExcerptHeader", EntityId::from(id)).into(),
|
||||||
|
Self::ExcerptFooter(id) => ("ExcerptFooter", EntityId::from(id)).into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for TransformBlockId {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Block(id) => write!(f, "Block({id:?})"),
|
||||||
|
Self::ExcerptHeader(id) => write!(f, "ExcerptHeader({id:?})"),
|
||||||
|
Self::ExcerptFooter(id) => write!(f, "ExcerptFooter({id:?})"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Whether the block should be considered above or below the anchor line
|
/// Whether the block should be considered above or below the anchor line
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub enum BlockDisposition {
|
pub enum BlockDisposition {
|
||||||
|
@ -157,6 +212,14 @@ impl BlockLike for TransformBlock {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TransformBlock {
|
impl TransformBlock {
|
||||||
|
pub fn id(&self) -> TransformBlockId {
|
||||||
|
match self {
|
||||||
|
TransformBlock::Custom(block) => TransformBlockId::Block(block.id),
|
||||||
|
TransformBlock::ExcerptHeader { id, .. } => TransformBlockId::ExcerptHeader(*id),
|
||||||
|
TransformBlock::ExcerptFooter { id, .. } => TransformBlockId::ExcerptFooter(*id),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn disposition(&self) -> BlockDisposition {
|
fn disposition(&self) -> BlockDisposition {
|
||||||
match self {
|
match self {
|
||||||
TransformBlock::Custom(block) => block.disposition,
|
TransformBlock::Custom(block) => block.disposition,
|
||||||
|
|
|
@ -68,12 +68,12 @@ use git::diff_hunk_to_display;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
div, impl_actions, point, prelude::*, px, relative, size, uniform_list, Action, AnyElement,
|
div, impl_actions, point, prelude::*, px, relative, size, uniform_list, Action, AnyElement,
|
||||||
AppContext, AsyncWindowContext, AvailableSpace, BackgroundExecutor, Bounds, ClipboardItem,
|
AppContext, AsyncWindowContext, AvailableSpace, BackgroundExecutor, Bounds, ClipboardItem,
|
||||||
Context, DispatchPhase, ElementId, EventEmitter, FocusHandle, FocusOutEvent, FocusableView,
|
Context, DispatchPhase, ElementId, EntityId, EventEmitter, FocusHandle, FocusOutEvent,
|
||||||
FontId, FontStyle, FontWeight, HighlightStyle, Hsla, InteractiveText, KeyContext,
|
FocusableView, FontId, FontStyle, FontWeight, HighlightStyle, Hsla, InteractiveText,
|
||||||
ListSizingBehavior, Model, MouseButton, PaintQuad, ParentElement, Pixels, Render, SharedString,
|
KeyContext, ListSizingBehavior, Model, MouseButton, PaintQuad, ParentElement, Pixels, Render,
|
||||||
Size, StrikethroughStyle, Styled, StyledText, Subscription, Task, TextStyle, UnderlineStyle,
|
SharedString, Size, StrikethroughStyle, Styled, StyledText, Subscription, Task, TextStyle,
|
||||||
UniformListScrollHandle, View, ViewContext, ViewInputHandler, VisualContext, WeakFocusHandle,
|
UnderlineStyle, UniformListScrollHandle, View, ViewContext, ViewInputHandler, VisualContext,
|
||||||
WeakView, WhiteSpace, WindowContext,
|
WeakFocusHandle, WeakView, WhiteSpace, WindowContext,
|
||||||
};
|
};
|
||||||
use highlight_matching_bracket::refresh_matching_bracket_highlights;
|
use highlight_matching_bracket::refresh_matching_bracket_highlights;
|
||||||
use hover_popover::{hide_hover, HoverState};
|
use hover_popover::{hide_hover, HoverState};
|
||||||
|
@ -9762,7 +9762,7 @@ impl Editor {
|
||||||
*block_id,
|
*block_id,
|
||||||
(
|
(
|
||||||
None,
|
None,
|
||||||
diagnostic_block_renderer(diagnostic.clone(), is_valid),
|
diagnostic_block_renderer(diagnostic.clone(), None, true, is_valid),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -9815,7 +9815,7 @@ impl Editor {
|
||||||
style: BlockStyle::Fixed,
|
style: BlockStyle::Fixed,
|
||||||
position: buffer.anchor_after(entry.range.start),
|
position: buffer.anchor_after(entry.range.start),
|
||||||
height: message_height,
|
height: message_height,
|
||||||
render: diagnostic_block_renderer(diagnostic, true),
|
render: diagnostic_block_renderer(diagnostic, None, true, true),
|
||||||
disposition: BlockDisposition::Below,
|
disposition: BlockDisposition::Below,
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
@ -12684,11 +12684,17 @@ impl InvalidationRegion for SnippetState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn diagnostic_block_renderer(diagnostic: Diagnostic, _is_valid: bool) -> RenderBlock {
|
pub fn diagnostic_block_renderer(
|
||||||
let (text_without_backticks, code_ranges) = highlight_diagnostic_message(&diagnostic);
|
diagnostic: Diagnostic,
|
||||||
|
max_message_rows: Option<u8>,
|
||||||
|
allow_closing: bool,
|
||||||
|
_is_valid: bool,
|
||||||
|
) -> RenderBlock {
|
||||||
|
let (text_without_backticks, code_ranges) =
|
||||||
|
highlight_diagnostic_message(&diagnostic, max_message_rows);
|
||||||
|
|
||||||
Box::new(move |cx: &mut BlockContext| {
|
Box::new(move |cx: &mut BlockContext| {
|
||||||
let group_id: SharedString = cx.block_id.to_string().into();
|
let group_id: SharedString = cx.transform_block_id.to_string().into();
|
||||||
|
|
||||||
let mut text_style = cx.text_style().clone();
|
let mut text_style = cx.text_style().clone();
|
||||||
text_style.color = diagnostic_style(diagnostic.severity, cx.theme().status());
|
text_style.color = diagnostic_style(diagnostic.severity, cx.theme().status());
|
||||||
|
@ -12700,14 +12706,15 @@ pub fn diagnostic_block_renderer(diagnostic: Diagnostic, _is_valid: bool) -> Ren
|
||||||
|
|
||||||
let multi_line_diagnostic = diagnostic.message.contains('\n');
|
let multi_line_diagnostic = diagnostic.message.contains('\n');
|
||||||
|
|
||||||
let buttons = |diagnostic: &Diagnostic, block_id: usize| {
|
let buttons = |diagnostic: &Diagnostic, block_id: TransformBlockId| {
|
||||||
if multi_line_diagnostic {
|
if multi_line_diagnostic {
|
||||||
v_flex()
|
v_flex()
|
||||||
} else {
|
} else {
|
||||||
h_flex()
|
h_flex()
|
||||||
}
|
}
|
||||||
.children(diagnostic.is_primary.then(|| {
|
.when(allow_closing, |div| {
|
||||||
IconButton::new(("close-block", block_id), IconName::XCircle)
|
div.children(diagnostic.is_primary.then(|| {
|
||||||
|
IconButton::new(("close-block", EntityId::from(block_id)), IconName::XCircle)
|
||||||
.icon_color(Color::Muted)
|
.icon_color(Color::Muted)
|
||||||
.size(ButtonSize::Compact)
|
.size(ButtonSize::Compact)
|
||||||
.style(ButtonStyle::Transparent)
|
.style(ButtonStyle::Transparent)
|
||||||
|
@ -12715,8 +12722,9 @@ pub fn diagnostic_block_renderer(diagnostic: Diagnostic, _is_valid: bool) -> Ren
|
||||||
.on_click(move |_click, cx| cx.dispatch_action(Box::new(Cancel)))
|
.on_click(move |_click, cx| cx.dispatch_action(Box::new(Cancel)))
|
||||||
.tooltip(|cx| Tooltip::for_action("Close Diagnostics", &Cancel, cx))
|
.tooltip(|cx| Tooltip::for_action("Close Diagnostics", &Cancel, cx))
|
||||||
}))
|
}))
|
||||||
|
})
|
||||||
.child(
|
.child(
|
||||||
IconButton::new(("copy-block", block_id), IconName::Copy)
|
IconButton::new(("copy-block", EntityId::from(block_id)), IconName::Copy)
|
||||||
.icon_color(Color::Muted)
|
.icon_color(Color::Muted)
|
||||||
.size(ButtonSize::Compact)
|
.size(ButtonSize::Compact)
|
||||||
.style(ButtonStyle::Transparent)
|
.style(ButtonStyle::Transparent)
|
||||||
|
@ -12729,12 +12737,12 @@ pub fn diagnostic_block_renderer(diagnostic: Diagnostic, _is_valid: bool) -> Ren
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
let icon_size = buttons(&diagnostic, cx.block_id)
|
let icon_size = buttons(&diagnostic, cx.transform_block_id)
|
||||||
.into_any_element()
|
.into_any_element()
|
||||||
.layout_as_root(AvailableSpace::min_size(), cx);
|
.layout_as_root(AvailableSpace::min_size(), cx);
|
||||||
|
|
||||||
h_flex()
|
h_flex()
|
||||||
.id(cx.block_id)
|
.id(cx.transform_block_id)
|
||||||
.group(group_id.clone())
|
.group(group_id.clone())
|
||||||
.relative()
|
.relative()
|
||||||
.size_full()
|
.size_full()
|
||||||
|
@ -12746,7 +12754,7 @@ pub fn diagnostic_block_renderer(diagnostic: Diagnostic, _is_valid: bool) -> Ren
|
||||||
.w(cx.anchor_x - cx.gutter_dimensions.width - icon_size.width)
|
.w(cx.anchor_x - cx.gutter_dimensions.width - icon_size.width)
|
||||||
.flex_shrink(),
|
.flex_shrink(),
|
||||||
)
|
)
|
||||||
.child(buttons(&diagnostic, cx.block_id))
|
.child(buttons(&diagnostic, cx.transform_block_id))
|
||||||
.child(div().flex().flex_shrink_0().child(
|
.child(div().flex().flex_shrink_0().child(
|
||||||
StyledText::new(text_without_backticks.clone()).with_highlights(
|
StyledText::new(text_without_backticks.clone()).with_highlights(
|
||||||
&text_style,
|
&text_style,
|
||||||
|
@ -12765,7 +12773,10 @@ pub fn diagnostic_block_renderer(diagnostic: Diagnostic, _is_valid: bool) -> Ren
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn highlight_diagnostic_message(diagnostic: &Diagnostic) -> (SharedString, Vec<Range<usize>>) {
|
pub fn highlight_diagnostic_message(
|
||||||
|
diagnostic: &Diagnostic,
|
||||||
|
mut max_message_rows: Option<u8>,
|
||||||
|
) -> (SharedString, Vec<Range<usize>>) {
|
||||||
let mut text_without_backticks = String::new();
|
let mut text_without_backticks = String::new();
|
||||||
let mut code_ranges = Vec::new();
|
let mut code_ranges = Vec::new();
|
||||||
|
|
||||||
|
@ -12777,18 +12788,45 @@ pub fn highlight_diagnostic_message(diagnostic: &Diagnostic) -> (SharedString, V
|
||||||
|
|
||||||
let mut prev_offset = 0;
|
let mut prev_offset = 0;
|
||||||
let mut in_code_block = false;
|
let mut in_code_block = false;
|
||||||
|
let mut newline_indices = diagnostic
|
||||||
|
.message
|
||||||
|
.match_indices('\n')
|
||||||
|
.map(|(ix, _)| ix)
|
||||||
|
.fuse()
|
||||||
|
.peekable();
|
||||||
for (ix, _) in diagnostic
|
for (ix, _) in diagnostic
|
||||||
.message
|
.message
|
||||||
.match_indices('`')
|
.match_indices('`')
|
||||||
.chain([(diagnostic.message.len(), "")])
|
.chain([(diagnostic.message.len(), "")])
|
||||||
{
|
{
|
||||||
|
let mut trimmed_ix = ix;
|
||||||
|
while let Some(newline_index) = newline_indices.peek() {
|
||||||
|
if *newline_index < ix {
|
||||||
|
if let Some(rows_left) = &mut max_message_rows {
|
||||||
|
if *rows_left == 0 {
|
||||||
|
trimmed_ix = newline_index.saturating_sub(1);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
*rows_left -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let _ = newline_indices.next();
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
let prev_len = text_without_backticks.len();
|
let prev_len = text_without_backticks.len();
|
||||||
text_without_backticks.push_str(&diagnostic.message[prev_offset..ix]);
|
let new_text = &diagnostic.message[prev_offset..trimmed_ix];
|
||||||
prev_offset = ix + 1;
|
text_without_backticks.push_str(new_text);
|
||||||
if in_code_block {
|
if in_code_block {
|
||||||
code_ranges.push(prev_len..text_without_backticks.len());
|
code_ranges.push(prev_len..text_without_backticks.len());
|
||||||
}
|
}
|
||||||
|
prev_offset = trimmed_ix + 1;
|
||||||
in_code_block = !in_code_block;
|
in_code_block = !in_code_block;
|
||||||
|
if trimmed_ix != ix {
|
||||||
|
text_without_backticks.push_str("...");
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
(text_without_backticks.into(), code_ranges)
|
(text_without_backticks.into(), code_ranges)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::editor_settings::ScrollBeyondLastLine;
|
use crate::editor_settings::ScrollBeyondLastLine;
|
||||||
|
use crate::TransformBlockId;
|
||||||
use crate::{
|
use crate::{
|
||||||
blame_entry_tooltip::{blame_entry_relative_timestamp, BlameEntryTooltip},
|
blame_entry_tooltip::{blame_entry_relative_timestamp, BlameEntryTooltip},
|
||||||
display_map::{
|
display_map::{
|
||||||
|
@ -31,7 +32,7 @@ use gpui::{
|
||||||
anchored, deferred, div, fill, outline, point, px, quad, relative, size, svg,
|
anchored, deferred, div, fill, outline, point, px, quad, relative, size, svg,
|
||||||
transparent_black, Action, AnchorCorner, AnyElement, AvailableSpace, Bounds, ClipboardItem,
|
transparent_black, Action, AnchorCorner, AnyElement, AvailableSpace, Bounds, ClipboardItem,
|
||||||
ContentMask, Corners, CursorStyle, DispatchPhase, Edges, Element, ElementInputHandler, Entity,
|
ContentMask, Corners, CursorStyle, DispatchPhase, Edges, Element, ElementInputHandler, Entity,
|
||||||
FontId, GlobalElementId, Hitbox, Hsla, InteractiveElement, IntoElement, Length,
|
EntityId, FontId, GlobalElementId, Hitbox, Hsla, InteractiveElement, IntoElement, Length,
|
||||||
ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, PaintQuad,
|
ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, PaintQuad,
|
||||||
ParentElement, Pixels, ScrollDelta, ScrollWheelEvent, ShapedLine, SharedString, Size,
|
ParentElement, Pixels, ScrollDelta, ScrollWheelEvent, ShapedLine, SharedString, Size,
|
||||||
StatefulInteractiveElement, Style, Styled, TextRun, TextStyle, TextStyleRefinement, View,
|
StatefulInteractiveElement, Style, Styled, TextRun, TextStyle, TextStyleRefinement, View,
|
||||||
|
@ -1939,7 +1940,6 @@ impl EditorElement {
|
||||||
line_layouts: &[LineWithInvisibles],
|
line_layouts: &[LineWithInvisibles],
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
) -> Vec<BlockLayout> {
|
) -> Vec<BlockLayout> {
|
||||||
let mut block_id = 0;
|
|
||||||
let (fixed_blocks, non_fixed_blocks) = snapshot
|
let (fixed_blocks, non_fixed_blocks) = snapshot
|
||||||
.blocks_in_range(rows.clone())
|
.blocks_in_range(rows.clone())
|
||||||
.partition::<Vec<_>, _>(|(_, block)| match block {
|
.partition::<Vec<_>, _>(|(_, block)| match block {
|
||||||
|
@ -1950,7 +1950,7 @@ impl EditorElement {
|
||||||
|
|
||||||
let render_block = |block: &TransformBlock,
|
let render_block = |block: &TransformBlock,
|
||||||
available_space: Size<AvailableSpace>,
|
available_space: Size<AvailableSpace>,
|
||||||
block_id: usize,
|
block_id: TransformBlockId,
|
||||||
block_row_start: DisplayRow,
|
block_row_start: DisplayRow,
|
||||||
cx: &mut WindowContext| {
|
cx: &mut WindowContext| {
|
||||||
let mut element = match block {
|
let mut element = match block {
|
||||||
|
@ -1974,7 +1974,7 @@ impl EditorElement {
|
||||||
gutter_dimensions,
|
gutter_dimensions,
|
||||||
line_height,
|
line_height,
|
||||||
em_width,
|
em_width,
|
||||||
block_id,
|
transform_block_id: block_id,
|
||||||
max_width: text_hitbox.size.width.max(*scroll_width),
|
max_width: text_hitbox.size.width.max(*scroll_width),
|
||||||
editor_style: &self.style,
|
editor_style: &self.style,
|
||||||
})
|
})
|
||||||
|
@ -2058,7 +2058,7 @@ impl EditorElement {
|
||||||
let header_padding = px(6.0);
|
let header_padding = px(6.0);
|
||||||
|
|
||||||
v_flex()
|
v_flex()
|
||||||
.id(("path excerpt header", block_id))
|
.id(("path excerpt header", EntityId::from(block_id)))
|
||||||
.size_full()
|
.size_full()
|
||||||
.p(header_padding)
|
.p(header_padding)
|
||||||
.child(
|
.child(
|
||||||
|
@ -2166,7 +2166,7 @@ impl EditorElement {
|
||||||
}))
|
}))
|
||||||
} else {
|
} else {
|
||||||
v_flex()
|
v_flex()
|
||||||
.id(("excerpt header", block_id))
|
.id(("excerpt header", EntityId::from(block_id)))
|
||||||
.size_full()
|
.size_full()
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
|
@ -2314,7 +2314,10 @@ impl EditorElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
TransformBlock::ExcerptFooter { id, .. } => {
|
TransformBlock::ExcerptFooter { id, .. } => {
|
||||||
let element = v_flex().id(("excerpt footer", block_id)).size_full().child(
|
let element = v_flex()
|
||||||
|
.id(("excerpt footer", EntityId::from(block_id)))
|
||||||
|
.size_full()
|
||||||
|
.child(
|
||||||
h_flex()
|
h_flex()
|
||||||
.justify_end()
|
.justify_end()
|
||||||
.flex_none()
|
.flex_none()
|
||||||
|
@ -2332,7 +2335,9 @@ impl EditorElement {
|
||||||
.group("")
|
.group("")
|
||||||
.hover(|style| {
|
.hover(|style| {
|
||||||
style.text_color(
|
style.text_color(
|
||||||
cx.theme().colors().editor_active_line_number,
|
cx.theme()
|
||||||
|
.colors()
|
||||||
|
.editor_active_line_number,
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
@ -2372,8 +2377,8 @@ impl EditorElement {
|
||||||
AvailableSpace::MinContent,
|
AvailableSpace::MinContent,
|
||||||
AvailableSpace::Definite(block.height() as f32 * line_height),
|
AvailableSpace::Definite(block.height() as f32 * line_height),
|
||||||
);
|
);
|
||||||
|
let block_id = block.id();
|
||||||
let (element, element_size) = render_block(block, available_space, block_id, row, cx);
|
let (element, element_size) = render_block(block, available_space, block_id, row, cx);
|
||||||
block_id += 1;
|
|
||||||
fixed_block_max_width = fixed_block_max_width.max(element_size.width + em_width);
|
fixed_block_max_width = fixed_block_max_width.max(element_size.width + em_width);
|
||||||
blocks.push(BlockLayout {
|
blocks.push(BlockLayout {
|
||||||
row,
|
row,
|
||||||
|
@ -2401,8 +2406,8 @@ impl EditorElement {
|
||||||
AvailableSpace::Definite(width),
|
AvailableSpace::Definite(width),
|
||||||
AvailableSpace::Definite(block.height() as f32 * line_height),
|
AvailableSpace::Definite(block.height() as f32 * line_height),
|
||||||
);
|
);
|
||||||
|
let block_id = block.id();
|
||||||
let (element, _) = render_block(block, available_space, block_id, row, cx);
|
let (element, _) = render_block(block, available_space, block_id, row, cx);
|
||||||
block_id += 1;
|
|
||||||
blocks.push(BlockLayout {
|
blocks.push(BlockLayout {
|
||||||
row,
|
row,
|
||||||
element,
|
element,
|
||||||
|
|
|
@ -34,6 +34,11 @@ impl FeatureFlag for TerminalInlineAssist {
|
||||||
const NAME: &'static str = "terminal-inline-assist";
|
const NAME: &'static str = "terminal-inline-assist";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct GroupedDiagnostics {}
|
||||||
|
impl FeatureFlag for GroupedDiagnostics {
|
||||||
|
const NAME: &'static str = "grouped-diagnostics";
|
||||||
|
}
|
||||||
|
|
||||||
pub trait FeatureFlagViewExt<V: 'static> {
|
pub trait FeatureFlagViewExt<V: 'static> {
|
||||||
fn observe_flag<T: FeatureFlag, F>(&mut self, callback: F) -> Subscription
|
fn observe_flag<T: FeatureFlag, F>(&mut self, callback: F) -> Subscription
|
||||||
where
|
where
|
||||||
|
|
|
@ -6,7 +6,7 @@ use clock::ReplicaId;
|
||||||
use collections::{BTreeMap, Bound, HashMap, HashSet};
|
use collections::{BTreeMap, Bound, HashMap, HashSet};
|
||||||
use futures::{channel::mpsc, SinkExt};
|
use futures::{channel::mpsc, SinkExt};
|
||||||
use git::diff::DiffHunk;
|
use git::diff::DiffHunk;
|
||||||
use gpui::{AppContext, EventEmitter, Model, ModelContext};
|
use gpui::{AppContext, EntityId, EventEmitter, Model, ModelContext};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use language::{
|
use language::{
|
||||||
char_kind,
|
char_kind,
|
||||||
|
@ -49,6 +49,12 @@ const NEWLINES: &[u8] = &[b'\n'; u8::MAX as usize];
|
||||||
#[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct ExcerptId(usize);
|
pub struct ExcerptId(usize);
|
||||||
|
|
||||||
|
impl From<ExcerptId> for EntityId {
|
||||||
|
fn from(id: ExcerptId) -> Self {
|
||||||
|
EntityId::from(id.0 as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// One or more [`Buffers`](Buffer) being edited in a single view.
|
/// One or more [`Buffers`](Buffer) being edited in a single view.
|
||||||
///
|
///
|
||||||
/// See <https://zed.dev/features#multi-buffers>
|
/// See <https://zed.dev/features#multi-buffers>
|
||||||
|
@ -302,6 +308,7 @@ struct ExcerptBytes<'a> {
|
||||||
reversed: bool,
|
reversed: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub enum ExpandExcerptDirection {
|
pub enum ExpandExcerptDirection {
|
||||||
Up,
|
Up,
|
||||||
Down,
|
Down,
|
||||||
|
@ -4679,7 +4686,7 @@ impl ToPointUtf16 for PointUtf16 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_excerpt_ranges<T>(
|
pub fn build_excerpt_ranges<T>(
|
||||||
buffer: &BufferSnapshot,
|
buffer: &BufferSnapshot,
|
||||||
ranges: &[Range<T>],
|
ranges: &[Range<T>],
|
||||||
context_line_count: u32,
|
context_line_count: u32,
|
||||||
|
|
|
@ -11720,7 +11720,7 @@ fn sort_search_matches(search_matches: &mut Vec<SearchMatchCandidate>, cx: &AppC
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compare_paths(
|
pub fn compare_paths(
|
||||||
(path_a, a_is_file): (&Path, bool),
|
(path_a, a_is_file): (&Path, bool),
|
||||||
(path_b, b_is_file): (&Path, bool),
|
(path_b, b_is_file): (&Path, bool),
|
||||||
) -> cmp::Ordering {
|
) -> cmp::Ordering {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue