diagnostics: Reduce cloning of DiagnosticEntry (#39193)

Release Notes:

- N/A *or* Added/Fixed/Improved ...
This commit is contained in:
Lukas Wirth
2025-09-30 11:41:49 +00:00
committed by GitHub
parent d8cafdf937
commit 0811d48a7a
14 changed files with 172 additions and 93 deletions
@@ -6,7 +6,7 @@ use assistant_slash_command::{
use fuzzy::{PathMatch, StringMatchCandidate}; use fuzzy::{PathMatch, StringMatchCandidate};
use gpui::{App, Entity, Task, WeakEntity}; use gpui::{App, Entity, Task, WeakEntity};
use language::{ use language::{
Anchor, BufferSnapshot, DiagnosticEntry, DiagnosticSeverity, LspAdapterDelegate, Anchor, BufferSnapshot, DiagnosticEntryRef, DiagnosticSeverity, LspAdapterDelegate,
OffsetRangeExt, ToOffset, OffsetRangeExt, ToOffset,
}; };
use project::{DiagnosticSummary, PathMatchCandidateSet, Project}; use project::{DiagnosticSummary, PathMatchCandidateSet, Project};
@@ -367,7 +367,7 @@ pub fn collect_buffer_diagnostics(
fn collect_diagnostic( fn collect_diagnostic(
output: &mut SlashCommandOutput, output: &mut SlashCommandOutput,
entry: &DiagnosticEntry<Anchor>, entry: &DiagnosticEntryRef<'_, Anchor>,
snapshot: &BufferSnapshot, snapshot: &BufferSnapshot,
include_warnings: bool, include_warnings: bool,
) { ) {
+8 -5
View File
@@ -15,7 +15,7 @@ use gpui::{
InteractiveElement, IntoElement, ParentElement, Render, SharedString, Styled, Subscription, InteractiveElement, IntoElement, ParentElement, Render, SharedString, Styled, Subscription,
Task, WeakEntity, Window, actions, div, Task, WeakEntity, Window, actions, div,
}; };
use language::{Buffer, DiagnosticEntry, Point}; use language::{Buffer, DiagnosticEntry, DiagnosticEntryRef, Point};
use project::{ use project::{
DiagnosticSummary, Event, Project, ProjectItem, ProjectPath, DiagnosticSummary, Event, Project, ProjectItem, ProjectPath,
project_settings::{DiagnosticSeverity, ProjectSettings}, project_settings::{DiagnosticSeverity, ProjectSettings},
@@ -350,7 +350,7 @@ impl BufferDiagnosticsEditor {
grouped grouped
.entry(entry.diagnostic.group_id) .entry(entry.diagnostic.group_id)
.or_default() .or_default()
.push(DiagnosticEntry { .push(DiagnosticEntryRef {
range: entry.range.to_point(&buffer_snapshot), range: entry.range.to_point(&buffer_snapshot),
diagnostic: entry.diagnostic, diagnostic: entry.diagnostic,
}) })
@@ -560,13 +560,16 @@ impl BufferDiagnosticsEditor {
}) })
} }
fn set_diagnostics(&mut self, diagnostics: &Vec<DiagnosticEntry<Anchor>>) { fn set_diagnostics(&mut self, diagnostics: &[DiagnosticEntryRef<'_, Anchor>]) {
self.diagnostics = diagnostics.clone(); self.diagnostics = diagnostics
.iter()
.map(DiagnosticEntryRef::to_owned)
.collect();
} }
fn diagnostics_are_unchanged( fn diagnostics_are_unchanged(
&self, &self,
diagnostics: &Vec<DiagnosticEntry<Anchor>>, diagnostics: &Vec<DiagnosticEntryRef<'_, Anchor>>,
snapshot: &BufferSnapshot, snapshot: &BufferSnapshot,
) -> bool { ) -> bool {
if self.diagnostics.len() != diagnostics.len() { if self.diagnostics.len() != diagnostics.len() {
+9 -13
View File
@@ -6,7 +6,7 @@ use editor::{
hover_popover::diagnostics_markdown_style, hover_popover::diagnostics_markdown_style,
}; };
use gpui::{AppContext, Entity, Focusable, WeakEntity}; use gpui::{AppContext, Entity, Focusable, WeakEntity};
use language::{BufferId, Diagnostic, DiagnosticEntry}; use language::{BufferId, Diagnostic, DiagnosticEntryRef};
use lsp::DiagnosticSeverity; use lsp::DiagnosticSeverity;
use markdown::{Markdown, MarkdownElement}; use markdown::{Markdown, MarkdownElement};
use settings::Settings; use settings::Settings;
@@ -24,7 +24,7 @@ pub struct DiagnosticRenderer;
impl DiagnosticRenderer { impl DiagnosticRenderer {
pub fn diagnostic_blocks_for_group( pub fn diagnostic_blocks_for_group(
diagnostic_group: Vec<DiagnosticEntry<Point>>, diagnostic_group: Vec<DiagnosticEntryRef<'_, Point>>,
buffer_id: BufferId, buffer_id: BufferId,
diagnostics_editor: Option<Arc<dyn DiagnosticsToolbarEditor>>, diagnostics_editor: Option<Arc<dyn DiagnosticsToolbarEditor>>,
cx: &mut App, cx: &mut App,
@@ -35,7 +35,7 @@ impl DiagnosticRenderer {
else { else {
return Vec::new(); return Vec::new();
}; };
let primary = diagnostic_group[primary_ix].clone(); let primary = &diagnostic_group[primary_ix];
let group_id = primary.diagnostic.group_id; let group_id = primary.diagnostic.group_id;
let mut results = vec![]; let mut results = vec![];
for entry in diagnostic_group.iter() { for entry in diagnostic_group.iter() {
@@ -123,7 +123,7 @@ impl DiagnosticRenderer {
impl editor::DiagnosticRenderer for DiagnosticRenderer { impl editor::DiagnosticRenderer for DiagnosticRenderer {
fn render_group( fn render_group(
&self, &self,
diagnostic_group: Vec<DiagnosticEntry<Point>>, diagnostic_group: Vec<DiagnosticEntryRef<'_, Point>>,
buffer_id: BufferId, buffer_id: BufferId,
snapshot: EditorSnapshot, snapshot: EditorSnapshot,
editor: WeakEntity<Editor>, editor: WeakEntity<Editor>,
@@ -152,19 +152,15 @@ impl editor::DiagnosticRenderer for DiagnosticRenderer {
fn render_hover( fn render_hover(
&self, &self,
diagnostic_group: Vec<DiagnosticEntry<Point>>, diagnostic_group: Vec<DiagnosticEntryRef<'_, Point>>,
range: Range<Point>, range: Range<Point>,
buffer_id: BufferId, buffer_id: BufferId,
cx: &mut App, cx: &mut App,
) -> Option<Entity<Markdown>> { ) -> Option<Entity<Markdown>> {
let blocks = Self::diagnostic_blocks_for_group(diagnostic_group, buffer_id, None, cx); let blocks = Self::diagnostic_blocks_for_group(diagnostic_group, buffer_id, None, cx);
blocks.into_iter().find_map(|block| { blocks
if block.initial_range == range { .into_iter()
Some(block.markdown) .find_map(|block| (block.initial_range == range).then(|| block.markdown))
} else {
None
}
})
} }
fn open_link( fn open_link(
@@ -189,7 +185,7 @@ pub(crate) struct DiagnosticBlock {
impl DiagnosticBlock { impl DiagnosticBlock {
pub fn render_block(&self, editor: WeakEntity<Editor>, bcx: &BlockContext) -> AnyElement { pub fn render_block(&self, editor: WeakEntity<Editor>, bcx: &BlockContext) -> AnyElement {
let cx = &bcx.app; let cx = &bcx.app;
let status_colors = bcx.app.theme().status(); let status_colors = cx.theme().status();
let max_width = bcx.em_width * 120.; let max_width = bcx.em_width * 120.;
+12 -5
View File
@@ -22,7 +22,8 @@ use gpui::{
Subscription, Task, WeakEntity, Window, actions, div, Subscription, Task, WeakEntity, Window, actions, div,
}; };
use language::{ use language::{
Bias, Buffer, BufferRow, BufferSnapshot, DiagnosticEntry, Point, ToTreeSitterPoint, Bias, Buffer, BufferRow, BufferSnapshot, DiagnosticEntry, DiagnosticEntryRef, Point,
ToTreeSitterPoint,
}; };
use project::{ use project::{
DiagnosticSummary, Project, ProjectPath, DiagnosticSummary, Project, ProjectPath,
@@ -412,8 +413,8 @@ impl ProjectDiagnosticsEditor {
fn diagnostics_are_unchanged( fn diagnostics_are_unchanged(
&self, &self,
existing: &Vec<DiagnosticEntry<text::Anchor>>, existing: &[DiagnosticEntry<text::Anchor>],
new: &Vec<DiagnosticEntry<text::Anchor>>, new: &[DiagnosticEntryRef<'_, text::Anchor>],
snapshot: &BufferSnapshot, snapshot: &BufferSnapshot,
) -> bool { ) -> bool {
if existing.len() != new.len() { if existing.len() != new.len() {
@@ -457,7 +458,13 @@ impl ProjectDiagnosticsEditor {
}) { }) {
return true; return true;
} }
this.diagnostics.insert(buffer_id, diagnostics.clone()); this.diagnostics.insert(
buffer_id,
diagnostics
.iter()
.map(DiagnosticEntryRef::to_owned)
.collect(),
);
false false
})?; })?;
if unchanged { if unchanged {
@@ -469,7 +476,7 @@ impl ProjectDiagnosticsEditor {
grouped grouped
.entry(entry.diagnostic.group_id) .entry(entry.diagnostic.group_id)
.or_default() .or_default()
.push(DiagnosticEntry { .push(DiagnosticEntryRef {
range: entry.range.to_point(&buffer_snapshot), range: entry.range.to_point(&buffer_snapshot),
diagnostic: entry.diagnostic, diagnostic: entry.diagnostic,
}) })
+8 -6
View File
@@ -14,12 +14,14 @@ use workspace::{StatusItemView, ToolbarItemEvent, Workspace, item::ItemHandle};
use crate::{Deploy, IncludeWarnings, ProjectDiagnosticsEditor}; use crate::{Deploy, IncludeWarnings, ProjectDiagnosticsEditor};
/// The status bar item that displays diagnostic counts.
pub struct DiagnosticIndicator { pub struct DiagnosticIndicator {
summary: project::DiagnosticSummary, summary: project::DiagnosticSummary,
active_editor: Option<WeakEntity<Editor>>,
workspace: WeakEntity<Workspace>, workspace: WeakEntity<Workspace>,
current_diagnostic: Option<Diagnostic>, current_diagnostic: Option<Diagnostic>,
active_editor: Option<WeakEntity<Editor>>,
_observe_active_editor: Option<Subscription>, _observe_active_editor: Option<Subscription>,
diagnostics_update: Task<()>, diagnostics_update: Task<()>,
diagnostic_summary_update: Task<()>, diagnostic_summary_update: Task<()>,
} }
@@ -73,10 +75,9 @@ impl Render for DiagnosticIndicator {
cx, cx,
) )
}) })
.on_click(cx.listener(|this, _, window, cx| { .on_click(
this.go_to_next_diagnostic(window, cx); cx.listener(|this, _, window, cx| this.go_to_next_diagnostic(window, cx)),
})) ),
.into_any_element(),
) )
} else { } else {
None None
@@ -177,7 +178,8 @@ impl DiagnosticIndicator {
.filter(|entry| !entry.range.is_empty()) .filter(|entry| !entry.range.is_empty())
.min_by_key(|entry| (entry.diagnostic.severity, entry.range.len())) .min_by_key(|entry| (entry.diagnostic.severity, entry.range.len()))
.map(|entry| entry.diagnostic); .map(|entry| entry.diagnostic);
if new_diagnostic != self.current_diagnostic { if new_diagnostic != self.current_diagnostic.as_ref() {
let new_diagnostic = new_diagnostic.cloned();
self.diagnostics_update = self.diagnostics_update =
cx.spawn_in(window, async move |diagnostics_indicator, cx| { cx.spawn_in(window, async move |diagnostics_indicator, cx| {
cx.background_executor() cx.background_executor()
+11 -14
View File
@@ -75,12 +75,9 @@ impl Render for ToolbarControls {
&ToggleDiagnosticsRefresh, &ToggleDiagnosticsRefresh,
)) ))
.on_click(cx.listener(move |toolbar_controls, _, _, cx| { .on_click(cx.listener(move |toolbar_controls, _, _, cx| {
match toolbar_controls.editor() { if let Some(editor) = toolbar_controls.editor() {
Some(editor) => { editor.stop_updating(cx);
editor.stop_updating(cx); cx.notify();
cx.notify();
}
None => {}
} }
})), })),
) )
@@ -95,11 +92,10 @@ impl Render for ToolbarControls {
&ToggleDiagnosticsRefresh, &ToggleDiagnosticsRefresh,
)) ))
.on_click(cx.listener({ .on_click(cx.listener({
move |toolbar_controls, _, window, cx| match toolbar_controls move |toolbar_controls, _, window, cx| {
.editor() if let Some(editor) = toolbar_controls.editor() {
{ editor.refresh_diagnostics(window, cx)
Some(editor) => editor.refresh_diagnostics(window, cx), }
None => {}
} }
})), })),
) )
@@ -110,9 +106,10 @@ impl Render for ToolbarControls {
.icon_color(warning_color) .icon_color(warning_color)
.shape(IconButtonShape::Square) .shape(IconButtonShape::Square)
.tooltip(Tooltip::text(warning_tooltip)) .tooltip(Tooltip::text(warning_tooltip))
.on_click(cx.listener(|this, _, window, cx| match &this.editor { .on_click(cx.listener(|this, _, window, cx| {
Some(editor) => editor.toggle_warnings(window, cx), if let Some(editor) = &this.editor {
None => {} editor.toggle_warnings(window, cx)
}
})), })),
) )
} }
+9 -9
View File
@@ -122,7 +122,7 @@ use itertools::{Either, Itertools};
use language::{ use language::{
AutoindentMode, BlockCommentConfig, BracketMatch, BracketPair, Buffer, BufferRow, AutoindentMode, BlockCommentConfig, BracketMatch, BracketPair, Buffer, BufferRow,
BufferSnapshot, Capability, CharClassifier, CharKind, CharScopeContext, CodeLabel, CursorShape, BufferSnapshot, Capability, CharClassifier, CharKind, CharScopeContext, CodeLabel, CursorShape,
DiagnosticEntry, DiffOptions, EditPredictionsMode, EditPreview, HighlightedText, IndentKind, DiagnosticEntryRef, DiffOptions, EditPredictionsMode, EditPreview, HighlightedText, IndentKind,
IndentSize, Language, OffsetRangeExt, Point, Runnable, RunnableRange, Selection, SelectionGoal, IndentSize, Language, OffsetRangeExt, Point, Runnable, RunnableRange, Selection, SelectionGoal,
TextObject, TransactionId, TreeSitterOptions, WordsQuery, TextObject, TransactionId, TreeSitterOptions, WordsQuery,
language_settings::{ language_settings::{
@@ -404,7 +404,7 @@ pub fn set_blame_renderer(renderer: impl BlameRenderer + 'static, cx: &mut App)
pub trait DiagnosticRenderer { pub trait DiagnosticRenderer {
fn render_group( fn render_group(
&self, &self,
diagnostic_group: Vec<DiagnosticEntry<Point>>, diagnostic_group: Vec<DiagnosticEntryRef<'_, Point>>,
buffer_id: BufferId, buffer_id: BufferId,
snapshot: EditorSnapshot, snapshot: EditorSnapshot,
editor: WeakEntity<Editor>, editor: WeakEntity<Editor>,
@@ -413,7 +413,7 @@ pub trait DiagnosticRenderer {
fn render_hover( fn render_hover(
&self, &self,
diagnostic_group: Vec<DiagnosticEntry<Point>>, diagnostic_group: Vec<DiagnosticEntryRef<'_, Point>>,
range: Range<Point>, range: Range<Point>,
buffer_id: BufferId, buffer_id: BufferId,
cx: &mut App, cx: &mut App,
@@ -15970,11 +15970,11 @@ impl Editor {
active_group_id = Some(active_group.group_id); active_group_id = Some(active_group.group_id);
} }
fn filtered( fn filtered<'a>(
snapshot: EditorSnapshot, snapshot: EditorSnapshot,
severity: GoToDiagnosticSeverityFilter, severity: GoToDiagnosticSeverityFilter,
diagnostics: impl Iterator<Item = DiagnosticEntry<usize>>, diagnostics: impl Iterator<Item = DiagnosticEntryRef<'a, usize>>,
) -> impl Iterator<Item = DiagnosticEntry<usize>> { ) -> impl Iterator<Item = DiagnosticEntryRef<'a, usize>> {
diagnostics diagnostics
.filter(move |entry| severity.matches(entry.diagnostic.severity)) .filter(move |entry| severity.matches(entry.diagnostic.severity))
.filter(|entry| entry.range.start != entry.range.end) .filter(|entry| entry.range.start != entry.range.end)
@@ -15998,7 +15998,7 @@ impl Editor {
.filter(|entry| entry.range.start >= selection.start), .filter(|entry| entry.range.start >= selection.start),
); );
let mut found: Option<DiagnosticEntry<usize>> = None; let mut found: Option<DiagnosticEntryRef<usize>> = None;
if direction == Direction::Prev { if direction == Direction::Prev {
'outer: for prev_diagnostics in [before.collect::<Vec<_>>(), after.collect::<Vec<_>>()] 'outer: for prev_diagnostics in [before.collect::<Vec<_>>(), after.collect::<Vec<_>>()]
{ {
@@ -17521,7 +17521,7 @@ impl Editor {
fn activate_diagnostics( fn activate_diagnostics(
&mut self, &mut self,
buffer_id: BufferId, buffer_id: BufferId,
diagnostic: DiagnosticEntry<usize>, diagnostic: DiagnosticEntryRef<'_, usize>,
window: &mut Window, window: &mut Window,
cx: &mut Context<Self>, cx: &mut Context<Self>,
) { ) {
@@ -17710,7 +17710,7 @@ impl Editor {
.map(|(line, _)| line) .map(|(line, _)| line)
.map(SharedString::new) .map(SharedString::new)
.unwrap_or_else(|| { .unwrap_or_else(|| {
SharedString::from(diagnostic_entry.diagnostic.message) SharedString::new(&*diagnostic_entry.diagnostic.message)
}); });
let start_anchor = snapshot.anchor_before(diagnostic_entry.range.start); let start_anchor = snapshot.anchor_before(diagnostic_entry.range.start);
let (Ok(i) | Err(i)) = inline_diagnostics let (Ok(i) | Err(i)) = inline_diagnostics
+1 -1
View File
@@ -371,7 +371,7 @@ fn show_hover(
this.update(cx, |_, cx| cx.observe(&markdown, |_, _, cx| cx.notify()))?; this.update(cx, |_, cx| cx.observe(&markdown, |_, _, cx| cx.notify()))?;
let local_diagnostic = DiagnosticEntry { let local_diagnostic = DiagnosticEntry {
diagnostic: local_diagnostic.diagnostic, diagnostic: local_diagnostic.diagnostic.to_owned(),
range: snapshot range: snapshot
.buffer_snapshot .buffer_snapshot
.anchor_before(local_diagnostic.range.start) .anchor_before(local_diagnostic.range.start)
+11 -9
View File
@@ -1,7 +1,7 @@
use crate::{ use crate::{
DebuggerTextObject, LanguageScope, Outline, OutlineConfig, RunnableCapture, RunnableTag, DebuggerTextObject, LanguageScope, Outline, OutlineConfig, RunnableCapture, RunnableTag,
TextObject, TreeSitterOptions, TextObject, TreeSitterOptions,
diagnostic_set::{DiagnosticEntry, DiagnosticGroup}, diagnostic_set::{DiagnosticEntry, DiagnosticEntryRef, DiagnosticGroup},
language_settings::{LanguageSettings, language_settings}, language_settings::{LanguageSettings, language_settings},
outline::OutlineItem, outline::OutlineItem,
syntax_map::{ syntax_map::{
@@ -4519,7 +4519,7 @@ impl BufferSnapshot {
&'a self, &'a self,
search_range: Range<T>, search_range: Range<T>,
reversed: bool, reversed: bool,
) -> impl 'a + Iterator<Item = DiagnosticEntry<O>> ) -> impl 'a + Iterator<Item = DiagnosticEntryRef<'a, O>>
where where
T: 'a + Clone + ToOffset, T: 'a + Clone + ToOffset,
O: 'a + FromAnchor, O: 'a + FromAnchor,
@@ -4552,11 +4552,13 @@ impl BufferSnapshot {
})?; })?;
iterators[next_ix] iterators[next_ix]
.next() .next()
.map(|DiagnosticEntry { range, diagnostic }| DiagnosticEntry { .map(
diagnostic, |DiagnosticEntryRef { range, diagnostic }| DiagnosticEntryRef {
range: FromAnchor::from_anchor(&range.start, self) diagnostic,
..FromAnchor::from_anchor(&range.end, self), range: FromAnchor::from_anchor(&range.start, self)
}) ..FromAnchor::from_anchor(&range.end, self),
},
)
}) })
} }
@@ -4572,7 +4574,7 @@ impl BufferSnapshot {
pub fn diagnostic_groups( pub fn diagnostic_groups(
&self, &self,
language_server_id: Option<LanguageServerId>, language_server_id: Option<LanguageServerId>,
) -> Vec<(LanguageServerId, DiagnosticGroup<Anchor>)> { ) -> Vec<(LanguageServerId, DiagnosticGroup<'_, Anchor>)> {
let mut groups = Vec::new(); let mut groups = Vec::new();
if let Some(language_server_id) = language_server_id { if let Some(language_server_id) = language_server_id {
@@ -4603,7 +4605,7 @@ impl BufferSnapshot {
pub fn diagnostic_group<O>( pub fn diagnostic_group<O>(
&self, &self,
group_id: usize, group_id: usize,
) -> impl Iterator<Item = DiagnosticEntry<O>> + '_ ) -> impl Iterator<Item = DiagnosticEntryRef<'_, O>> + use<'_, O>
where where
O: FromAnchor + 'static, O: FromAnchor + 'static,
{ {
+83 -13
View File
@@ -34,19 +34,66 @@ pub struct DiagnosticEntry<T> {
pub diagnostic: Diagnostic, pub diagnostic: Diagnostic,
} }
/// A single diagnostic in a set. Generic over its range type, because
/// the diagnostics are stored internally as [`Anchor`]s, but can be
/// resolved to different coordinates types like [`usize`] byte offsets or
/// [`Point`](gpui::Point)s.
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
pub struct DiagnosticEntryRef<'a, T> {
/// The range of the buffer where the diagnostic applies.
pub range: Range<T>,
/// The information about the diagnostic.
pub diagnostic: &'a Diagnostic,
}
impl<T: PartialEq> PartialEq<DiagnosticEntry<T>> for DiagnosticEntryRef<'_, T> {
fn eq(&self, other: &DiagnosticEntry<T>) -> bool {
self.range == other.range && *self.diagnostic == other.diagnostic
}
}
impl<T: PartialEq> PartialEq<DiagnosticEntryRef<'_, T>> for DiagnosticEntry<T> {
fn eq(&self, other: &DiagnosticEntryRef<'_, T>) -> bool {
self.range == other.range && self.diagnostic == *other.diagnostic
}
}
impl<T: Clone> DiagnosticEntryRef<'_, T> {
pub fn to_owned(&self) -> DiagnosticEntry<T> {
DiagnosticEntry {
range: self.range.clone(),
diagnostic: self.diagnostic.clone(),
}
}
}
impl<'a> DiagnosticEntryRef<'a, Anchor> {
/// Converts the [DiagnosticEntry] to a different buffer coordinate type.
pub fn resolve<O: FromAnchor>(
&self,
buffer: &text::BufferSnapshot,
) -> DiagnosticEntryRef<'a, O> {
DiagnosticEntryRef {
range: O::from_anchor(&self.range.start, buffer)
..O::from_anchor(&self.range.end, buffer),
diagnostic: &self.diagnostic,
}
}
}
/// A group of related diagnostics, ordered by their start position /// A group of related diagnostics, ordered by their start position
/// in the buffer. /// in the buffer.
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
pub struct DiagnosticGroup<T> { pub struct DiagnosticGroup<'a, T> {
/// The diagnostics. /// The diagnostics.
pub entries: Vec<DiagnosticEntry<T>>, pub entries: Vec<DiagnosticEntryRef<'a, T>>,
/// The index into `entries` where the primary diagnostic is stored. /// The index into `entries` where the primary diagnostic is stored.
pub primary_ix: usize, pub primary_ix: usize,
} }
impl DiagnosticGroup<Anchor> { impl<'a> DiagnosticGroup<'a, Anchor> {
/// Converts the entries in this [`DiagnosticGroup`] to a different buffer coordinate type. /// Converts the entries in this [`DiagnosticGroup`] to a different buffer coordinate type.
pub fn resolve<O: FromAnchor>(&self, buffer: &text::BufferSnapshot) -> DiagnosticGroup<O> { pub fn resolve<O: FromAnchor>(&self, buffer: &text::BufferSnapshot) -> DiagnosticGroup<'a, O> {
DiagnosticGroup { DiagnosticGroup {
entries: self entries: self
.entries .entries
@@ -84,6 +131,23 @@ impl DiagnosticEntry<PointUtf16> {
}) })
} }
} }
impl DiagnosticEntryRef<'_, PointUtf16> {
/// Returns a raw LSP diagnostic used to provide diagnostic context to LSP
/// codeAction request
pub fn to_lsp_diagnostic_stub(&self) -> Result<lsp::Diagnostic> {
let range = range_to_lsp(self.range.clone())?;
Ok(lsp::Diagnostic {
range,
code: self.diagnostic.code.clone(),
severity: Some(self.diagnostic.severity),
source: self.diagnostic.source.clone(),
message: self.diagnostic.message.clone(),
data: self.diagnostic.data.clone(),
..Default::default()
})
}
}
impl DiagnosticSet { impl DiagnosticSet {
/// Constructs a [DiagnosticSet] from a sequence of entries, ordered by /// Constructs a [DiagnosticSet] from a sequence of entries, ordered by
@@ -138,7 +202,7 @@ impl DiagnosticSet {
buffer: &'a text::BufferSnapshot, buffer: &'a text::BufferSnapshot,
inclusive: bool, inclusive: bool,
reversed: bool, reversed: bool,
) -> impl 'a + Iterator<Item = DiagnosticEntry<O>> ) -> impl 'a + Iterator<Item = DiagnosticEntryRef<'a, O>>
where where
T: 'a + ToOffset, T: 'a + ToOffset,
O: FromAnchor, O: FromAnchor,
@@ -179,10 +243,10 @@ impl DiagnosticSet {
} }
/// Adds all of this set's diagnostic groups to the given output vector. /// Adds all of this set's diagnostic groups to the given output vector.
pub fn groups( pub fn groups<'a>(
&self, &'a self,
language_server_id: LanguageServerId, language_server_id: LanguageServerId,
output: &mut Vec<(LanguageServerId, DiagnosticGroup<Anchor>)>, output: &mut Vec<(LanguageServerId, DiagnosticGroup<'a, Anchor>)>,
buffer: &text::BufferSnapshot, buffer: &text::BufferSnapshot,
) { ) {
let mut groups = HashMap::default(); let mut groups = HashMap::default();
@@ -190,7 +254,10 @@ impl DiagnosticSet {
groups groups
.entry(entry.diagnostic.group_id) .entry(entry.diagnostic.group_id)
.or_insert(Vec::new()) .or_insert(Vec::new())
.push(entry.clone()); .push(DiagnosticEntryRef {
range: entry.range.clone(),
diagnostic: &entry.diagnostic,
});
} }
let start_ix = output.len(); let start_ix = output.len();
@@ -224,7 +291,7 @@ impl DiagnosticSet {
&'a self, &'a self,
group_id: usize, group_id: usize,
buffer: &'a text::BufferSnapshot, buffer: &'a text::BufferSnapshot,
) -> impl 'a + Iterator<Item = DiagnosticEntry<O>> { ) -> impl 'a + Iterator<Item = DiagnosticEntryRef<'a, O>> {
self.iter() self.iter()
.filter(move |entry| entry.diagnostic.group_id == group_id) .filter(move |entry| entry.diagnostic.group_id == group_id)
.map(|entry| entry.resolve(buffer)) .map(|entry| entry.resolve(buffer))
@@ -247,11 +314,14 @@ impl sum_tree::Item for DiagnosticEntry<Anchor> {
impl DiagnosticEntry<Anchor> { impl DiagnosticEntry<Anchor> {
/// Converts the [DiagnosticEntry] to a different buffer coordinate type. /// Converts the [DiagnosticEntry] to a different buffer coordinate type.
pub fn resolve<O: FromAnchor>(&self, buffer: &text::BufferSnapshot) -> DiagnosticEntry<O> { pub fn resolve<'a, O: FromAnchor>(
DiagnosticEntry { &'a self,
buffer: &text::BufferSnapshot,
) -> DiagnosticEntryRef<'a, O> {
DiagnosticEntryRef {
range: O::from_anchor(&self.range.start, buffer) range: O::from_anchor(&self.range.start, buffer)
..O::from_anchor(&self.range.end, buffer), ..O::from_anchor(&self.range.end, buffer),
diagnostic: self.diagnostic.clone(), diagnostic: &self.diagnostic,
} }
} }
} }
+1 -1
View File
@@ -75,7 +75,7 @@ use util::serde::default_true;
pub use buffer::Operation; pub use buffer::Operation;
pub use buffer::*; pub use buffer::*;
pub use diagnostic_set::{DiagnosticEntry, DiagnosticGroup}; pub use diagnostic_set::{DiagnosticEntry, DiagnosticEntryRef, DiagnosticGroup};
pub use language_registry::{ pub use language_registry::{
AvailableLanguage, BinaryStatus, LanguageNotFound, LanguageQueries, LanguageRegistry, AvailableLanguage, BinaryStatus, LanguageNotFound, LanguageQueries, LanguageRegistry,
QUERY_FILENAME_PREFIXES, QUERY_FILENAME_PREFIXES,
+8 -8
View File
@@ -17,7 +17,7 @@ use gpui::{App, AppContext as _, Context, Entity, EntityId, EventEmitter, Task};
use itertools::Itertools; use itertools::Itertools;
use language::{ use language::{
AutoindentMode, Buffer, BufferChunks, BufferRow, BufferSnapshot, Capability, CharClassifier, AutoindentMode, Buffer, BufferChunks, BufferRow, BufferSnapshot, Capability, CharClassifier,
CharKind, CharScopeContext, Chunk, CursorShape, DiagnosticEntry, DiskState, File, CharKind, CharScopeContext, Chunk, CursorShape, DiagnosticEntryRef, DiskState, File,
IndentGuideSettings, IndentSize, Language, LanguageScope, OffsetRangeExt, OffsetUtf16, Outline, IndentGuideSettings, IndentSize, Language, LanguageScope, OffsetRangeExt, OffsetUtf16, Outline,
OutlineItem, Point, PointUtf16, Selection, TextDimension, TextObject, ToOffset as _, OutlineItem, Point, PointUtf16, Selection, TextDimension, TextObject, ToOffset as _,
ToPoint as _, TransactionId, TreeSitterOptions, Unclipped, ToPoint as _, TransactionId, TreeSitterOptions, Unclipped,
@@ -6007,7 +6007,7 @@ impl MultiBufferSnapshot {
&self, &self,
buffer_id: BufferId, buffer_id: BufferId,
group_id: usize, group_id: usize,
) -> impl Iterator<Item = DiagnosticEntry<Point>> + '_ { ) -> impl Iterator<Item = DiagnosticEntryRef<'_, Point>> + '_ {
self.lift_buffer_metadata(Point::zero()..self.max_point(), move |buffer, range| { self.lift_buffer_metadata(Point::zero()..self.max_point(), move |buffer, range| {
if buffer.remote_id() != buffer_id { if buffer.remote_id() != buffer_id {
return None; return None;
@@ -6016,16 +6016,16 @@ impl MultiBufferSnapshot {
buffer buffer
.diagnostics_in_range(range, false) .diagnostics_in_range(range, false)
.filter(move |diagnostic| diagnostic.diagnostic.group_id == group_id) .filter(move |diagnostic| diagnostic.diagnostic.group_id == group_id)
.map(move |DiagnosticEntry { diagnostic, range }| (range, diagnostic)), .map(move |DiagnosticEntryRef { diagnostic, range }| (range, diagnostic)),
) )
}) })
.map(|(range, diagnostic, _)| DiagnosticEntry { diagnostic, range }) .map(|(range, diagnostic, _)| DiagnosticEntryRef { diagnostic, range })
} }
pub fn diagnostics_in_range<'a, T>( pub fn diagnostics_in_range<'a, T>(
&'a self, &'a self,
range: Range<T>, range: Range<T>,
) -> impl Iterator<Item = DiagnosticEntry<T>> + 'a ) -> impl Iterator<Item = DiagnosticEntryRef<'a, T>> + 'a
where where
T: 'a T: 'a
+ text::ToOffset + text::ToOffset
@@ -6042,13 +6042,13 @@ impl MultiBufferSnapshot {
.map(|entry| (entry.range, entry.diagnostic)), .map(|entry| (entry.range, entry.diagnostic)),
) )
}) })
.map(|(range, diagnostic, _)| DiagnosticEntry { diagnostic, range }) .map(|(range, diagnostic, _)| DiagnosticEntryRef { diagnostic, range })
} }
pub fn diagnostics_with_buffer_ids_in_range<'a, T>( pub fn diagnostics_with_buffer_ids_in_range<'a, T>(
&'a self, &'a self,
range: Range<T>, range: Range<T>,
) -> impl Iterator<Item = (BufferId, DiagnosticEntry<T>)> + 'a ) -> impl Iterator<Item = (BufferId, DiagnosticEntryRef<'a, T>)> + 'a
where where
T: 'a T: 'a
+ text::ToOffset + text::ToOffset
@@ -6065,7 +6065,7 @@ impl MultiBufferSnapshot {
.map(|entry| (entry.range, entry.diagnostic)), .map(|entry| (entry.range, entry.diagnostic)),
) )
}) })
.map(|(range, diagnostic, b)| (b.buffer_id, DiagnosticEntry { diagnostic, range })) .map(|(range, diagnostic, b)| (b.buffer_id, DiagnosticEntryRef { diagnostic, range }))
} }
pub fn syntax_ancestor<T: ToOffset>( pub fn syntax_ancestor<T: ToOffset>(
+8 -7
View File
@@ -20,9 +20,10 @@ use git2::RepositoryInitOptions;
use gpui::{App, BackgroundExecutor, SemanticVersion, UpdateGlobal}; use gpui::{App, BackgroundExecutor, SemanticVersion, UpdateGlobal};
use itertools::Itertools; use itertools::Itertools;
use language::{ use language::{
Diagnostic, DiagnosticEntry, DiagnosticSet, DiagnosticSourceKind, DiskState, FakeLspAdapter, Diagnostic, DiagnosticEntry, DiagnosticEntryRef, DiagnosticSet, DiagnosticSourceKind,
LanguageConfig, LanguageMatcher, LanguageName, LineEnding, ManifestName, ManifestProvider, DiskState, FakeLspAdapter, LanguageConfig, LanguageMatcher, LanguageName, LineEnding,
ManifestQuery, OffsetRangeExt, Point, ToPoint, ToolchainList, ToolchainLister, ManifestName, ManifestProvider, ManifestQuery, OffsetRangeExt, Point, ToPoint, ToolchainList,
ToolchainLister,
language_settings::{LanguageSettingsContent, language_settings}, language_settings::{LanguageSettingsContent, language_settings},
tree_sitter_rust, tree_sitter_typescript, tree_sitter_rust, tree_sitter_typescript,
}; };
@@ -1847,9 +1848,9 @@ async fn test_disk_based_diagnostics_progress(cx: &mut gpui::TestAppContext) {
.collect::<Vec<_>>(); .collect::<Vec<_>>();
assert_eq!( assert_eq!(
diagnostics, diagnostics,
&[DiagnosticEntry { &[DiagnosticEntryRef {
range: Point::new(0, 9)..Point::new(0, 10), range: Point::new(0, 9)..Point::new(0, 10),
diagnostic: Diagnostic { diagnostic: &Diagnostic {
severity: lsp::DiagnosticSeverity::ERROR, severity: lsp::DiagnosticSeverity::ERROR,
message: "undefined variable 'A'".to_string(), message: "undefined variable 'A'".to_string(),
group_id: 0, group_id: 0,
@@ -2024,7 +2025,7 @@ async fn test_restarting_server_with_diagnostics_published(cx: &mut gpui::TestAp
buffer buffer
.snapshot() .snapshot()
.diagnostics_in_range::<_, usize>(0..1, false) .diagnostics_in_range::<_, usize>(0..1, false)
.map(|entry| entry.diagnostic.message) .map(|entry| entry.diagnostic.message.clone())
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
["the message".to_string()] ["the message".to_string()]
); );
@@ -2050,7 +2051,7 @@ async fn test_restarting_server_with_diagnostics_published(cx: &mut gpui::TestAp
buffer buffer
.snapshot() .snapshot()
.diagnostics_in_range::<_, usize>(0..1, false) .diagnostics_in_range::<_, usize>(0..1, false)
.map(|entry| entry.diagnostic.message) .map(|entry| entry.diagnostic.message.clone())
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
Vec::<String>::new(), Vec::<String>::new(),
); );
+1
View File
@@ -9,6 +9,7 @@ use ui::{h_flex, prelude::*};
use util::ResultExt; use util::ResultExt;
pub trait StatusItemView: Render { pub trait StatusItemView: Render {
/// Event callback that is triggered when the active pane item changes.
fn set_active_pane_item( fn set_active_pane_item(
&mut self, &mut self,
active_pane_item: Option<&dyn crate::ItemHandle>, active_pane_item: Option<&dyn crate::ItemHandle>,