Add inlay hint settings

Kirill Bulatov created

Change summary

assets/settings/default.json         | 10 +++++++++
crates/editor/src/editor.rs          | 33 ++++++++++++++++++++++++++---
crates/editor/src/editor_settings.rs | 18 ++++++++++++++++
crates/editor/src/inlay_cache.rs     | 18 ++++++++++++++++
4 files changed, 75 insertions(+), 4 deletions(-)

Detailed changes

assets/settings/default.json 🔗

@@ -73,6 +73,16 @@
     // Whether to show git diff indicators in the scrollbar.
     "git_diff": true
   },
+  // Inlay hint related settings
+  "inlay_hints": {
+    // Global switch to toggle hints on and off, switched off by default.
+    "enabled":  false,
+    // Toggle certain types of hints on and off, all switched on by default.
+    "show_type_hints": true,
+    "show_parameter_hints":  true,
+    // Corresponds to null/None LSP hint type value.
+    "show_other_hints": true
+  },
   "project_panel": {
     // Whether to show the git status in the project panel.
     "git_status": true,

crates/editor/src/editor.rs 🔗

@@ -74,7 +74,8 @@ pub use multi_buffer::{
 use multi_buffer::{MultiBufferChunks, ToOffsetUtf16};
 use ordered_float::OrderedFloat;
 use project::{
-    FormatTrigger, InlayHint, Location, LocationLink, Project, ProjectPath, ProjectTransaction,
+    FormatTrigger, InlayHint, InlayHintKind, Location, LocationLink, Project, ProjectPath,
+    ProjectTransaction,
 };
 use scroll::{
     autoscroll::Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide,
@@ -2590,11 +2591,20 @@ impl Editor {
         }
     }
 
-    fn refresh_inlays(&self, cx: &mut ViewContext<Self>) {
+    fn refresh_inlays(&mut self, cx: &mut ViewContext<Self>) {
         if self.mode != EditorMode::Full {
             return;
         }
 
+        let inlay_hint_settings = settings::get::<EditorSettings>(cx).inlay_hints;
+        if !inlay_hint_settings.enabled {
+            let to_remove = self.inlay_cache.clear();
+            self.display_map.update(cx, |display_map, cx| {
+                display_map.splice_inlays(to_remove, Vec::new(), cx);
+            });
+            return;
+        }
+
         struct InlayRequestKey {
             buffer_path: PathBuf,
             buffer_version: Global,
@@ -2619,6 +2629,9 @@ impl Editor {
                     excerpt_id,
                 };
 
+                // TODO kb split this into 2 different steps:
+                // 1. cache population
+                // 2. cache querying + hint filters on top (needs to store previous filter settings)
                 let task = cx.spawn(|editor, mut cx| async move {
                     if inlays_up_to_date {
                         anyhow::Ok((key, None))
@@ -2646,9 +2659,20 @@ impl Editor {
                             Some(task) => {
                                 match task.await.context("inlays for buffer task")? {
                                     Some(mut new_inlays) => {
+                                        let mut allowed_inlay_hint_types = Vec::new();
+                                        if inlay_hint_settings.show_type_hints {
+                                            allowed_inlay_hint_types.push(Some(InlayHintKind::Type));
+                                        }
+                                        if inlay_hint_settings.show_parameter_hints {
+                                            allowed_inlay_hint_types.push(Some(InlayHintKind::Parameter));
+                                        }
+                                        if inlay_hint_settings.show_other_hints {
+                                            allowed_inlay_hint_types.push(None);
+                                        }
                                         new_inlays.retain(|inlay| {
                                             let inlay_offset = inlay.position.offset;
-                                            query_start <= inlay_offset && inlay_offset <= query_end
+                                            allowed_inlay_hint_types.contains(&inlay.kind)
+                                                && query_start <= inlay_offset && inlay_offset <= query_end
                                         });
                                         Some(new_inlays)
                                     },
@@ -2713,7 +2737,7 @@ impl Editor {
                     to_remove,
                     to_insert,
                 } = editor.update(&mut cx, |editor, _| {
-                    editor.inlay_cache.update_inlays(inlay_updates)
+                    dbg!(editor.inlay_cache.update_inlays(inlay_updates))
                 })?;
 
                 editor.update(&mut cx, |editor, cx| {
@@ -7318,6 +7342,7 @@ impl Editor {
 
     fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
         self.refresh_copilot_suggestions(true, cx);
+        self.refresh_inlays(cx);
     }
 
     pub fn set_searchable(&mut self, searchable: bool) {

crates/editor/src/editor_settings.rs 🔗

@@ -9,6 +9,7 @@ pub struct EditorSettings {
     pub show_completions_on_input: bool,
     pub use_on_type_format: bool,
     pub scrollbar: Scrollbar,
+    pub inlay_hints: InlayHints,
 }
 
 #[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
@@ -17,6 +18,14 @@ pub struct Scrollbar {
     pub git_diff: bool,
 }
 
+#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
+pub struct InlayHints {
+    pub enabled: bool,
+    pub show_type_hints: bool,
+    pub show_parameter_hints: bool,
+    pub show_other_hints: bool,
+}
+
 #[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
 #[serde(rename_all = "snake_case")]
 pub enum ShowScrollbar {
@@ -33,6 +42,7 @@ pub struct EditorSettingsContent {
     pub show_completions_on_input: Option<bool>,
     pub use_on_type_format: Option<bool>,
     pub scrollbar: Option<ScrollbarContent>,
+    pub inlay_hints: Option<InlayHintsContent>,
 }
 
 #[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
@@ -41,6 +51,14 @@ pub struct ScrollbarContent {
     pub git_diff: Option<bool>,
 }
 
+#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
+pub struct InlayHintsContent {
+    pub enabled: Option<bool>,
+    pub show_type_hints: Option<bool>,
+    pub show_parameter_hints: Option<bool>,
+    pub show_other_hints: Option<bool>,
+}
+
 impl Setting for EditorSettings {
     const KEY: Option<&'static str> = None;
 

crates/editor/src/inlay_cache.rs 🔗

@@ -229,4 +229,22 @@ impl InlayCache {
             to_insert,
         }
     }
+
+    pub fn clear(&mut self) -> Vec<InlayId> {
+        self.inlays_per_buffer
+            .drain()
+            .map(|(_, buffer_inlays)| {
+                buffer_inlays
+                    .inlays_per_excerpts
+                    .into_iter()
+                    .map(|(_, excerpt_inlays)| {
+                        excerpt_inlays
+                            .into_ordered_elements()
+                            .map(|(_, (id, _))| id)
+                    })
+                    .flatten()
+            })
+            .flatten()
+            .collect()
+    }
 }