editor: Ensure completion menu scrollbar does not become stale (#44536)

Finn Evers created

Only by reusing the previous scroll handle, we can ensure that both the
scrollbar remains usable and also that the scrollbar does not flicker.
Previously, the scrollbar would hold the reference to an outdated
handle.

I tried invalidating the handle the scrollbar uses, but that leads to
flickering, which is worse. Hence, let's just reuse the scrollbar here.

Release Notes:

- Fixed an issue where the scrollbar would become stale in the code
completions menu after the items were updated.

Change summary

crates/editor/src/code_context_menus.rs | 13 +++++++++++--
crates/editor/src/editor.rs             | 10 +++++++++-
2 files changed, 20 insertions(+), 3 deletions(-)

Detailed changes

crates/editor/src/code_context_menus.rs 🔗

@@ -206,6 +206,13 @@ impl CodeContextMenu {
             CodeContextMenu::CodeActions(_) => (),
         }
     }
+
+    pub fn primary_scroll_handle(&self) -> UniformListScrollHandle {
+        match self {
+            CodeContextMenu::Completions(menu) => menu.scroll_handle.clone(),
+            CodeContextMenu::CodeActions(menu) => menu.scroll_handle.clone(),
+        }
+    }
 }
 
 pub enum ContextMenuOrigin {
@@ -303,6 +310,7 @@ impl CompletionsMenu {
         is_incomplete: bool,
         buffer: Entity<Buffer>,
         completions: Box<[Completion]>,
+        scroll_handle: Option<UniformListScrollHandle>,
         display_options: CompletionDisplayOptions,
         snippet_sort_order: SnippetSortOrder,
         language_registry: Option<Arc<LanguageRegistry>>,
@@ -332,7 +340,7 @@ impl CompletionsMenu {
             selected_item: 0,
             filter_task: Task::ready(()),
             cancel_filter: Arc::new(AtomicBool::new(false)),
-            scroll_handle: UniformListScrollHandle::new(),
+            scroll_handle: scroll_handle.unwrap_or_else(UniformListScrollHandle::new),
             scroll_handle_aside: ScrollHandle::new(),
             resolve_completions: true,
             last_rendered_range: RefCell::new(None).into(),
@@ -354,6 +362,7 @@ impl CompletionsMenu {
         choices: &Vec<String>,
         selection: Range<Anchor>,
         buffer: Entity<Buffer>,
+        scroll_handle: Option<UniformListScrollHandle>,
         snippet_sort_order: SnippetSortOrder,
     ) -> Self {
         let completions = choices
@@ -404,7 +413,7 @@ impl CompletionsMenu {
             selected_item: 0,
             filter_task: Task::ready(()),
             cancel_filter: Arc::new(AtomicBool::new(false)),
-            scroll_handle: UniformListScrollHandle::new(),
+            scroll_handle: scroll_handle.unwrap_or_else(UniformListScrollHandle::new),
             scroll_handle_aside: ScrollHandle::new(),
             resolve_completions: false,
             show_completion_documentation: false,

crates/editor/src/editor.rs 🔗

@@ -5882,6 +5882,11 @@ impl Editor {
                         is_incomplete,
                         buffer.clone(),
                         completions.into(),
+                        editor
+                            .context_menu()
+                            .borrow_mut()
+                            .as_ref()
+                            .map(|menu| menu.primary_scroll_handle()),
                         display_options,
                         snippet_sort_order,
                         languages,
@@ -10016,13 +10021,16 @@ impl Editor {
 
         let id = post_inc(&mut self.next_completion_id);
         let snippet_sort_order = EditorSettings::get_global(cx).snippet_sort_order;
-        *self.context_menu.borrow_mut() = Some(CodeContextMenu::Completions(
+        let mut context_menu = self.context_menu.borrow_mut();
+        let old_menu = context_menu.take();
+        *context_menu = Some(CodeContextMenu::Completions(
             CompletionsMenu::new_snippet_choices(
                 id,
                 true,
                 choices,
                 selection,
                 buffer,
+                old_menu.map(|menu| menu.primary_scroll_handle()),
                 snippet_sort_order,
             ),
         ));