Make invisibles display configurable

Kirill Bulatov created

Change summary

assets/settings/default.json     |  8 ++++++++
crates/editor/src/element.rs     |  9 +++++++++
crates/gpui/src/elements/text.rs |  4 +++-
crates/settings/src/settings.rs  | 15 +++++++++++++++
4 files changed, 35 insertions(+), 1 deletion(-)

Detailed changes

assets/settings/default.json 🔗

@@ -33,6 +33,14 @@
   // Controls whether copilot provides suggestion immediately
   // or waits for a `copilot::Toggle`
   "show_copilot_suggestions": true,
+  // Whether to show tabs and spaces in the editor.
+  // This setting can take two values:
+  //
+  // 1. Do not draw any tabs or spaces (default):
+  //   "none"
+  // 2. Draw all invisible symbols:
+  //   "all"
+  "show_invisibles": "none",
   // Whether the screen sharing icon is shown in the os status bar.
   "show_call_status_icon": true,
   // Whether to use language servers to provide code intelligence.

crates/editor/src/element.rs 🔗

@@ -1365,6 +1365,14 @@ impl EditorElement {
                         is_tab: chunk.is_tab,
                     }
                 });
+
+            let settings = cx.global::<Settings>();
+            let show_invisibles = settings
+                .editor_overrides
+                .show_invisibles
+                .or(settings.editor_defaults.show_invisibles)
+                .unwrap_or_default()
+                == settings::ShowInvisibles::All;
             layout_highlighted_chunks(
                 chunks,
                 &style.text,
@@ -1372,6 +1380,7 @@ impl EditorElement {
                 cx.font_cache(),
                 MAX_LINE_LEN,
                 rows.len() as usize,
+                show_invisibles,
             )
         }
     }

crates/gpui/src/elements/text.rs 🔗

@@ -129,6 +129,7 @@ impl<V: View> Element<V> for Text {
             &cx.font_cache,
             usize::MAX,
             self.text.matches('\n').count() + 1,
+            false,
         );
 
         // If line wrapping is enabled, wrap each of the shaped lines.
@@ -365,6 +366,7 @@ pub fn layout_highlighted_chunks<'a>(
     font_cache: &Arc<FontCache>,
     max_line_len: usize,
     max_line_count: usize,
+    show_invisibles: bool,
 ) -> Vec<Line> {
     let mut layouts = Vec::with_capacity(max_line_count);
     let mut line = String::new();
@@ -416,7 +418,7 @@ pub fn layout_highlighted_chunks<'a>(
                         underline: text_style.underline,
                     },
                 ));
-                if highlighted_chunk.is_tab {
+                if show_invisibles && highlighted_chunk.is_tab {
                     invisibles.push(Invisible::Tab {
                         range: line.len()..line.len() + line_chunk.len(),
                     });

crates/settings/src/settings.rs 🔗

@@ -173,6 +173,7 @@ pub struct EditorSettings {
     pub formatter: Option<Formatter>,
     pub enable_language_server: Option<bool>,
     pub show_copilot_suggestions: Option<bool>,
+    pub show_invisibles: Option<ShowInvisibles>,
 }
 
 #[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
@@ -446,6 +447,14 @@ pub struct FeaturesContent {
     pub copilot: Option<bool>,
 }
 
+#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
+#[serde(rename_all = "snake_case")]
+pub enum ShowInvisibles {
+    #[default]
+    None,
+    All,
+}
+
 impl Settings {
     pub fn initial_user_settings_content(assets: &'static impl AssetSource) -> Cow<'static, str> {
         match assets.load(INITIAL_USER_SETTINGS_ASSET_PATH).unwrap() {
@@ -507,6 +516,7 @@ impl Settings {
                 formatter: required(defaults.editor.formatter),
                 enable_language_server: required(defaults.editor.enable_language_server),
                 show_copilot_suggestions: required(defaults.editor.show_copilot_suggestions),
+                show_invisibles: required(defaults.editor.show_invisibles),
             },
             editor_overrides: Default::default(),
             copilot: CopilotSettings {
@@ -657,6 +667,10 @@ impl Settings {
         self.language_setting(language, |settings| settings.tab_size)
     }
 
+    pub fn show_invisibles(&self, language: Option<&str>) -> ShowInvisibles {
+        self.language_setting(language, |settings| settings.show_invisibles)
+    }
+
     pub fn hard_tabs(&self, language: Option<&str>) -> bool {
         self.language_setting(language, |settings| settings.hard_tabs)
     }
@@ -793,6 +807,7 @@ impl Settings {
                 formatter: Some(Formatter::LanguageServer),
                 enable_language_server: Some(true),
                 show_copilot_suggestions: Some(true),
+                show_invisibles: Some(ShowInvisibles::None),
             },
             editor_overrides: Default::default(),
             copilot: Default::default(),