editor: Add a setting to show a scrollbar in completion menu (#41849)

Anthony Eid created

A user on Discord requested this feature:
https://discord.com/channels/869392257814519848/1434188637389717556/1434188637389717556

I added a scrollbar setting called `completion_menu_scrollbar` to the
completion menu and defaulted it to "Never" to match past behavior.

Release Notes:

- editor: Add `editor.completion_menu_scrollbar` setting to show a
scrollbar in the completion menu

Change summary

assets/settings/default.json                   | 13 +++++++
crates/editor/src/code_context_menus.rs        | 35 +++++++++++++++++--
crates/editor/src/editor_settings.rs           |  2 +
crates/gpui/src/elements/uniform_list.rs       |  2 -
crates/settings/src/settings_content/editor.rs | 13 +++++++
crates/settings/src/vscode_import.rs           |  1 
crates/settings_ui/src/page_data.rs            | 13 +++++++
crates/ui/src/components/scrollbar.rs          |  2 
docs/src/visual-customization.md               |  2 +
9 files changed, 76 insertions(+), 7 deletions(-)

Detailed changes

assets/settings/default.json 🔗

@@ -255,6 +255,19 @@
   // Whether to display inline and alongside documentation for items in the
   // completions menu
   "show_completion_documentation": true,
+  // When to show the scrollbar in the completion menu.
+  // This setting can take four values:
+  //
+  // 1. Show the scrollbar if there's important information or
+  //    follow the system's configured behavior
+  //   "auto"
+  // 2. Match the system's configured behavior:
+  //    "system"
+  // 3. Always show the scrollbar:
+  //    "always"
+  // 4. Never show the scrollbar:
+  //    "never" (default)
+  "completion_menu_scrollbar": "never",
   // Show method signatures in the editor, when inside parentheses.
   "auto_signature_help": false,
   // Whether to show the signature help after completion or a bracket pair inserted.

crates/editor/src/code_context_menus.rs 🔗

@@ -28,10 +28,12 @@ use std::{
     rc::Rc,
 };
 use task::ResolvedTask;
-use ui::{Color, IntoElement, ListItem, Pixels, Popover, Styled, prelude::*};
+use ui::{
+    Color, IntoElement, ListItem, Pixels, Popover, ScrollAxes, Scrollbars, Styled, WithScrollbar,
+    prelude::*,
+};
 use util::ResultExt;
 
-use crate::CodeActionSource;
 use crate::hover_popover::{hover_markdown_style, open_markdown_url};
 use crate::{
     CodeActionProvider, CompletionId, CompletionItemKind, CompletionProvider, DisplayRow, Editor,
@@ -39,7 +41,8 @@ use crate::{
     actions::{ConfirmCodeAction, ConfirmCompletion},
     split_words, styled_runs_for_code_label,
 };
-use settings::SnippetSortOrder;
+use crate::{CodeActionSource, EditorSettings};
+use settings::{Settings, SnippetSortOrder};
 
 pub const MENU_GAP: Pixels = px(4.);
 pub const MENU_ASIDE_X_PADDING: Pixels = px(16.);
@@ -261,6 +264,20 @@ impl Drop for CompletionsMenu {
     }
 }
 
+struct CompletionMenuScrollBarSetting;
+
+impl ui::scrollbars::GlobalSetting for CompletionMenuScrollBarSetting {
+    fn get_value(_cx: &App) -> &Self {
+        &Self
+    }
+}
+
+impl ui::scrollbars::ScrollbarVisibility for CompletionMenuScrollBarSetting {
+    fn visibility(&self, cx: &App) -> ui::scrollbars::ShowScrollbar {
+        EditorSettings::get_global(cx).completion_menu_scrollbar
+    }
+}
+
 impl CompletionsMenu {
     pub fn new(
         id: CompletionId,
@@ -898,7 +915,17 @@ impl CompletionsMenu {
             }
         });
 
-        Popover::new().child(list).into_any_element()
+        Popover::new()
+            .child(
+                div().child(list).custom_scrollbars(
+                    Scrollbars::for_settings::<CompletionMenuScrollBarSetting>()
+                        .show_along(ScrollAxes::Vertical)
+                        .tracked_scroll_handle(self.scroll_handle.clone()),
+                    window,
+                    cx,
+                ),
+            )
+            .into_any_element()
     }
 
     fn render_aside(

crates/editor/src/editor_settings.rs 🔗

@@ -55,6 +55,7 @@ pub struct EditorSettings {
     pub drag_and_drop_selection: DragAndDropSelection,
     pub lsp_document_colors: DocumentColorsRenderMode,
     pub minimum_contrast_for_highlights: f32,
+    pub completion_menu_scrollbar: ShowScrollbar,
 }
 #[derive(Debug, Clone)]
 pub struct Jupyter {
@@ -268,6 +269,7 @@ impl Settings for EditorSettings {
             },
             lsp_document_colors: editor.lsp_document_colors.unwrap(),
             minimum_contrast_for_highlights: editor.minimum_contrast_for_highlights.unwrap().0,
+            completion_menu_scrollbar: editor.completion_menu_scrollbar.map(Into::into).unwrap(),
         }
     }
 }

crates/settings/src/settings_content/editor.rs 🔗

@@ -197,6 +197,19 @@ pub struct EditorSettingsContent {
     ///
     /// Default: [`DocumentColorsRenderMode::Inlay`]
     pub lsp_document_colors: Option<DocumentColorsRenderMode>,
+    /// When to show the scrollbar in the completion menu.
+    /// This setting can take four values:
+    ///
+    /// 1. Show the scrollbar if there's important information or
+    ///    follow the system's configured behavior
+    ///   "auto"
+    /// 2. Match the system's configured behavior:
+    ///    "system"
+    /// 3. Always show the scrollbar:
+    ///    "always"
+    /// 4. Never show the scrollbar:
+    ///    "never" (default)
+    pub completion_menu_scrollbar: Option<ShowScrollbar>,
 }
 
 // Toolbar related settings

crates/settings/src/vscode_import.rs 🔗

@@ -299,6 +299,7 @@ impl VsCodeSettings {
             toolbar: None,
             use_smartcase_search: self.read_bool("search.smartCase"),
             vertical_scroll_margin: self.read_f32("editor.cursorSurroundingLines"),
+            completion_menu_scrollbar: None,
         }
     }
 

crates/settings_ui/src/page_data.rs 🔗

@@ -6541,6 +6541,19 @@ fn language_settings_data() -> Vec<SettingsPageItem> {
             metadata: None,
             files: USER | PROJECT,
         }),
+        SettingsPageItem::SettingItem(SettingItem {
+            title: "Completion Menu Scrollbar",
+            description: "When to show the scrollbar in the completion menu.",
+            field: Box::new(SettingField {
+                json_path: Some("editor.completion_menu_scrollbar"),
+                pick: |settings_content| settings_content.editor.completion_menu_scrollbar.as_ref(),
+                write: |settings_content, value| {
+                    settings_content.editor.completion_menu_scrollbar = value;
+                },
+            }),
+            metadata: None,
+            files: USER,
+        }),
         SettingsPageItem::SectionHeader("Inlay Hints"),
         SettingsPageItem::SettingItem(SettingItem {
             title: "Enabled",

crates/ui/src/components/scrollbar.rs 🔗

@@ -392,7 +392,7 @@ pub struct Scrollbars<T: ScrollableHandle = ScrollHandle> {
 
 impl Scrollbars {
     pub fn new(show_along: ScrollAxes) -> Self {
-        Self::new_with_setting(show_along, |_| ShowScrollbar::Always)
+        Self::new_with_setting(show_along, |_| ShowScrollbar::default())
     }
 
     pub fn for_settings<S: ScrollbarVisibility>() -> Scrollbars {

docs/src/visual-customization.md 🔗

@@ -368,6 +368,8 @@ TBD: Centered layout related settings
 
   // How to render LSP `textDocument/documentColor` colors in the editor.
   "lsp_document_colors": "inlay",        // none, inlay, border, background
+  // When to show the scrollbar in the completion menu.
+  "completion_menu_scrollbar": "never", // auto, system, always, never
 ```
 
 ### Edit Predictions {#editor-ai}