From 5094380c83b73fadad8fb40359ad5b30bef2d442 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 18 Nov 2021 16:55:18 +0100 Subject: [PATCH] Enhance keyboard navigation when showing next diagnostic Co-Authored-By: Nathan Sobo --- crates/buffer/src/anchor.rs | 6 ++ crates/editor/src/display_map.rs | 5 +- crates/editor/src/lib.rs | 112 ++++++++++++++++++++++++------- crates/language/src/lib.rs | 4 +- 4 files changed, 99 insertions(+), 28 deletions(-) diff --git a/crates/buffer/src/anchor.rs b/crates/buffer/src/anchor.rs index 7b765a948b..4fdd387be0 100644 --- a/crates/buffer/src/anchor.rs +++ b/crates/buffer/src/anchor.rs @@ -527,6 +527,7 @@ impl<'a> sum_tree::SeekTarget<'a, AnchorRangeMultimapSummary, FullOffsetRange> f pub trait AnchorRangeExt { fn cmp<'a>(&self, b: &Range, buffer: impl Into>) -> Result; + fn to_offset<'a>(&self, content: impl Into>) -> Range; } impl AnchorRangeExt for Range { @@ -537,4 +538,9 @@ impl AnchorRangeExt for Range { ord @ _ => ord, }) } + + fn to_offset<'a>(&self, content: impl Into>) -> Range { + let content = content.into(); + self.start.to_offset(&content)..self.end.to_offset(&content) + } } diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index 61fe1fa00a..3354d76e61 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -4,7 +4,8 @@ mod patch; mod tab_map; mod wrap_map; -use block_map::{BlockId, BlockMap, BlockPoint}; +pub use block_map::{BlockDisposition, BlockId, BlockProperties, BufferRows, Chunks}; +use block_map::{BlockMap, BlockPoint}; use buffer::Rope; use fold_map::{FoldMap, ToFoldPoint as _}; use gpui::{fonts::FontId, Entity, ModelContext, ModelHandle}; @@ -15,8 +16,6 @@ use tab_map::TabMap; use theme::SyntaxTheme; use wrap_map::WrapMap; -pub use block_map::{BlockDisposition, BlockProperties, BufferRows, Chunks}; - pub trait ToDisplayPoint { fn to_display_point(&self, map: &DisplayMapSnapshot) -> DisplayPoint; } diff --git a/crates/editor/src/lib.rs b/crates/editor/src/lib.rs index f97d6166e2..2afb18d1de 100644 --- a/crates/editor/src/lib.rs +++ b/crates/editor/src/lib.rs @@ -24,6 +24,7 @@ use smol::Timer; use std::{ cell::RefCell, cmp::{self, Ordering}, + collections::HashSet, iter, mem, ops::{Range, RangeInclusive}, rc::Rc, @@ -304,6 +305,7 @@ pub struct Editor { add_selections_state: Option, autoclose_stack: Vec, select_larger_syntax_node_stack: Vec]>>, + active_diagnostics: Option, scroll_position: Vector2F, scroll_top_anchor: Anchor, autoscroll_requested: bool, @@ -336,6 +338,12 @@ struct BracketPairState { pair: BracketPair, } +#[derive(Debug)] +struct ActiveDiagnosticGroup { + primary_range: Range, + block_ids: HashSet, +} + #[derive(Serialize, Deserialize)] struct ClipboardSelection { len: usize, @@ -423,6 +431,7 @@ impl Editor { add_selections_state: None, autoclose_stack: Default::default(), select_larger_syntax_node_stack: Vec::new(), + active_diagnostics: None, build_settings, scroll_position: Vector2F::zero(), scroll_top_anchor: Anchor::min(), @@ -2205,36 +2214,93 @@ impl Editor { } pub fn show_next_diagnostic(&mut self, _: &ShowNextDiagnostic, cx: &mut ViewContext) { - let selection = self.newest_selection(cx); + let selection = self.newest_selection::(cx); let buffer = self.buffer.read(cx.as_ref()); - let diagnostic_group_id = dbg!(buffer - .diagnostics_in_range::<_, usize>(selection.head()..buffer.len()) - .filter(|(_, diagnostic)| diagnostic.is_primary) - .next()) - .map(|(_, diagnostic)| diagnostic.group_id); + let active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| { + active_diagnostics + .primary_range + .to_offset(buffer) + .to_inclusive() + }); + let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() { + if active_primary_range.contains(&selection.head()) { + *active_primary_range.end() + } else { + selection.head() + } + } else { + selection.head() + }; - if let Some(group_id) = diagnostic_group_id { - self.display_map.update(cx, |display_map, cx| { - let buffer = self.buffer.read(cx); - let diagnostic_group = buffer - .diagnostic_group::(group_id) - .map(|(range, diagnostic)| (range, diagnostic.message.clone())) - .collect::>(); + loop { + let next_group = buffer + .diagnostics_in_range::<_, usize>(search_start..buffer.len()) + .filter(|(_, diagnostic)| diagnostic.is_primary) + .skip_while(|(range, _)| { + Some(range.end) == active_primary_range.as_ref().map(|r| *r.end()) + }) + .next() + .map(|(range, diagnostic)| (range, diagnostic.group_id)); - dbg!(group_id, &diagnostic_group); + if let Some((primary_range, group_id)) = next_group { + self.dismiss_diagnostics(cx); + self.active_diagnostics = self.display_map.update(cx, |display_map, cx| { + let buffer = self.buffer.read(cx); + let diagnostic_group = buffer + .diagnostic_group::(group_id) + .map(|(range, diagnostic)| (range, diagnostic.message.clone())) + .collect::>(); + let primary_range = buffer.anchor_after(primary_range.start) + ..buffer.anchor_before(primary_range.end); - display_map.insert_blocks( - diagnostic_group - .iter() - .map(|(range, message)| BlockProperties { - position: range.start, - text: message.as_str(), - runs: vec![], - disposition: BlockDisposition::Above, - }), + let block_ids = display_map + .insert_blocks( + diagnostic_group + .iter() + .map(|(range, message)| BlockProperties { + position: range.start, + text: message.as_str(), + runs: vec![], + disposition: BlockDisposition::Above, + }), + cx, + ) + .into_iter() + .collect(); + + Some(ActiveDiagnosticGroup { + primary_range, + block_ids, + }) + }); + + self.update_selections( + vec![Selection { + id: selection.id, + start: primary_range.start, + end: primary_range.start, + reversed: false, + goal: SelectionGoal::None, + }], + true, cx, ); + break; + } else if search_start == 0 { + break; + } else { + // Cycle around to the start of the buffer. + search_start = 0; + } + } + } + + fn dismiss_diagnostics(&mut self, cx: &mut ViewContext) { + if let Some(active_diagnostic_group) = self.active_diagnostics.take() { + self.display_map.update(cx, |display_map, cx| { + display_map.remove_blocks(active_diagnostic_group.block_ids, cx); }); + cx.notify(); } } diff --git a/crates/language/src/lib.rs b/crates/language/src/lib.rs index f810c75cc4..0755ed9571 100644 --- a/crates/language/src/lib.rs +++ b/crates/language/src/lib.rs @@ -816,7 +816,7 @@ impl Buffer { pub fn diagnostics_in_range<'a, T, O>( &'a self, - range: Range, + search_range: Range, ) -> impl Iterator, &Diagnostic)> + 'a where T: 'a + ToOffset, @@ -824,7 +824,7 @@ impl Buffer { { let content = self.content(); self.diagnostics - .intersecting_ranges(range, content, true) + .intersecting_ranges(search_range, content, true) .map(move |(_, range, diagnostic)| (range, diagnostic)) }