WIP

Antonio Scandurra created

Change summary

crates/editor/src/display_map.rs           | 12 ++++
crates/editor/src/display_map/block_map.rs | 23 +++++++--
crates/editor/src/lib.rs                   | 60 ++++++++++++++++++++++--
3 files changed, 84 insertions(+), 11 deletions(-)

Detailed changes

crates/editor/src/display_map.rs 🔗

@@ -10,7 +10,10 @@ use buffer::Rope;
 use fold_map::{FoldMap, ToFoldPoint as _};
 use gpui::{fonts::FontId, AppContext, Entity, ModelContext, ModelHandle};
 use language::{Anchor, Buffer, Point, ToOffset, ToPoint};
-use std::{collections::HashSet, ops::Range};
+use std::{
+    collections::{HashMap, HashSet},
+    ops::Range,
+};
 use sum_tree::Bias;
 use tab_map::TabMap;
 use theme::{BlockStyle, SyntaxTheme};
@@ -128,6 +131,13 @@ impl DisplayMap {
         block_map.insert(blocks, cx)
     }
 
+    pub fn restyle_blocks<F>(&mut self, styles: HashMap<BlockId, Option<F>>)
+    where
+        F: 'static + Fn(&AppContext) -> BlockStyle,
+    {
+        self.block_map.restyle(styles);
+    }
+
     pub fn remove_blocks(&mut self, ids: HashSet<BlockId>, cx: &mut ModelContext<Self>) {
         let (snapshot, edits) = self.fold_map.read(cx);
         let (snapshot, edits) = self.tab_map.sync(snapshot, edits);

crates/editor/src/display_map/block_map.rs 🔗

@@ -8,7 +8,7 @@ use language::{Buffer, Chunk};
 use parking_lot::Mutex;
 use std::{
     cmp::{self, Ordering},
-    collections::HashSet,
+    collections::{HashMap, HashSet},
     fmt::Debug,
     iter,
     ops::{Deref, Range},
@@ -53,7 +53,7 @@ pub struct Block {
     position: Anchor,
     text: Rope,
     build_runs: Option<Arc<dyn Fn(&AppContext) -> Vec<(usize, HighlightStyle)>>>,
-    build_style: Option<Arc<dyn Fn(&AppContext) -> BlockStyle>>,
+    build_style: Mutex<Option<Arc<dyn Fn(&AppContext) -> BlockStyle>>>,
     disposition: BlockDisposition,
 }
 
@@ -332,6 +332,19 @@ impl BlockMap {
         drop(cursor);
         *transforms = new_transforms;
     }
+
+    pub fn restyle<F>(&mut self, mut styles: HashMap<BlockId, Option<F>>)
+    where
+        F: 'static + Fn(&AppContext) -> BlockStyle,
+    {
+        for block in &self.blocks {
+            if let Some(build_style) = styles.remove(&block.id) {
+                *block.build_style.lock() = build_style.map(|build_style| {
+                    Arc::new(build_style) as Arc<dyn Fn(&AppContext) -> BlockStyle>
+                });
+            }
+        }
+    }
 }
 
 fn push_isomorphic(tree: &mut SumTree<Transform>, rows: u32) {
@@ -421,7 +434,7 @@ impl<'a> BlockMapWriter<'a> {
                     position,
                     text: block.text.into(),
                     build_runs: block.build_runs,
-                    build_style: block.build_style,
+                    build_style: Mutex::new(block.build_style),
                     disposition: block.disposition,
                 }),
             );
@@ -896,7 +909,7 @@ impl<'a> Iterator for BufferRows<'a> {
         if let Some(block) = &transform.block {
             let style = self
                 .cx
-                .and_then(|cx| block.build_style.as_ref().map(|f| f(cx)));
+                .and_then(|cx| block.build_style.lock().as_ref().map(|f| f(cx)));
             Some(DisplayRow::Block(block.id, style))
         } else {
             Some(self.input_buffer_rows.next().unwrap())
@@ -1017,7 +1030,7 @@ mod tests {
                 id: BlockId(0),
                 position: Anchor::min(),
                 text: "one!\ntwo three\nfour".into(),
-                build_style: None,
+                build_style: Mutex::new(None),
                 build_runs: Some(Arc::new(move |_| {
                     vec![(3, red.into()), (6, Default::default()), (5, blue.into())]
                 })),

crates/editor/src/lib.rs 🔗

@@ -24,7 +24,7 @@ use smol::Timer;
 use std::{
     cell::RefCell,
     cmp::{self, Ordering},
-    collections::HashSet,
+    collections::{HashMap, HashSet},
     iter, mem,
     ops::{Range, RangeInclusive},
     rc::Rc,
@@ -341,8 +341,10 @@ struct BracketPairState {
 #[derive(Debug)]
 struct ActiveDiagnosticGroup {
     primary_range: Range<Anchor>,
+    primary_message: String,
+    blocks: HashMap<BlockId, Diagnostic>,
     group_range: Range<Anchor>,
-    block_ids: HashSet<BlockId>,
+    is_valid: bool,
 }
 
 #[derive(Serialize, Deserialize)]
@@ -2268,13 +2270,17 @@ impl Editor {
                         ..buffer.anchor_before(group_end);
                     let primary_range = buffer.anchor_after(primary_range.start)
                         ..buffer.anchor_before(primary_range.end);
+                    let mut primary_message = None;
 
-                    let block_ids = display_map
+                    let blocks = display_map
                         .insert_blocks(
                             diagnostic_group.iter().map(|(range, diagnostic)| {
                                 let build_settings = self.build_settings.clone();
                                 let message_len = diagnostic.message.len();
                                 let severity = diagnostic.severity;
+                                if diagnostic.is_primary {
+                                    primary_message = Some(diagnostic.message.clone());
+                                }
                                 BlockProperties {
                                     position: range.start,
                                     text: diagnostic.message.as_str(),
@@ -2303,12 +2309,19 @@ impl Editor {
                             cx,
                         )
                         .into_iter()
+                        .zip(
+                            diagnostic_group
+                                .into_iter()
+                                .map(|(_, diagnostic)| diagnostic),
+                        )
                         .collect();
 
                     Some(ActiveDiagnosticGroup {
                         primary_range,
+                        primary_message: primary_message.unwrap(),
                         group_range,
-                        block_ids,
+                        blocks,
+                        is_valid: true,
                     })
                 });
 
@@ -2333,10 +2346,46 @@ impl Editor {
         }
     }
 
+    fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
+        if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
+            let buffer = self.buffer.read(cx);
+            let primary_range_start = active_diagnostics.primary_range.start.to_offset(buffer);
+            let matching_diagnostic = buffer
+                .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone())
+                .find_map(|(range, diagnostic)| {
+                    if diagnostic.is_primary
+                        && range.start == primary_range_start
+                        && diagnostic.message == active_diagnostics.primary_message
+                    {
+                        Some(diagnostic.group_id)
+                    } else {
+                        None
+                    }
+                });
+            if let Some(matching_diagnostic) = matching_diagnostic {
+            } else if active_diagnostics.is_valid {
+                let mut new_styles = HashMap::new();
+                for (block_id, diagnostic) in &active_diagnostics.blocks {
+                    let build_settings = self.build_settings.clone();
+                    let severity = diagnostic.severity;
+                    new_styles.insert(
+                        *block_id,
+                        Some(move |cx: &AppContext| {
+                            let settings = build_settings.borrow()(cx);
+                            diagnostic_style(severity, false, &settings.style).block
+                        }),
+                    );
+                }
+                self.display_map
+                    .update(cx, |display_map, _| display_map.restyle_blocks(new_styles));
+            }
+        }
+    }
+
     fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
         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);
+                display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
             });
             cx.notify();
         }
@@ -2799,6 +2848,7 @@ impl Editor {
     }
 
     fn on_buffer_changed(&mut self, _: ModelHandle<Buffer>, cx: &mut ViewContext<Self>) {
+        self.refresh_active_diagnostics(cx);
         cx.notify();
     }