Make unnecessary code fade configurable (#14442)

Matthew D. Scholefield and Marshall Bowers created

This PR allows configuring the intensity of code fade applied to unused
code (relates to #4785).

_Note: Right now I included it as a top level config which might be a
little out of place (but is easiest to instrument). Open for suggestions
on where else it would be better suited for._

_Note 2: I am unfamiliar with the codebase. Feel free to either close
this PR and re-implement in a better way or suggest high level changes
if I'm approaching anything wrong :)._

Release Notes:

- Added `unnecesary_code_fade` setting to control how strongly to fade
unused code.

---------

Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>

Change summary

assets/settings/default.json           |  2 ++
crates/assistant/src/prompt_library.rs |  1 +
crates/editor/src/display_map.rs       |  4 +---
crates/editor/src/editor.rs            |  4 ++++
crates/theme/src/settings.rs           | 10 ++++++++++
docs/src/configuring-zed.md            | 21 +++++++++++++++++++++
6 files changed, 39 insertions(+), 3 deletions(-)

Detailed changes

assets/settings/default.json 🔗

@@ -64,6 +64,8 @@
   "ui_font_weight": 400,
   // The default font size for text in the UI
   "ui_font_size": 16,
+  // How much to fade out unused code.
+  "unnecessary_code_fade": 0.3,
   // The factor to grow the active pane by. Defaults to 1.0
   // which gives the same size as all other panes.
   "active_pane_magnification": 1.0,

crates/editor/src/display_map.rs 🔗

@@ -74,8 +74,6 @@ pub enum FoldStatus {
 
 pub type RenderFoldToggle = Arc<dyn Fn(FoldStatus, &mut WindowContext) -> AnyElement>;
 
-const UNNECESSARY_CODE_FADE: f32 = 0.3;
-
 pub trait ToDisplayPoint {
     fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint;
 }
@@ -691,7 +689,7 @@ impl DisplaySnapshot {
             let mut diagnostic_highlight = HighlightStyle::default();
 
             if chunk.is_unnecessary {
-                diagnostic_highlight.fade_out = Some(UNNECESSARY_CODE_FADE);
+                diagnostic_highlight.fade_out = Some(editor_style.unnecessary_code_fade);
             }
 
             if let Some(severity) = chunk.diagnostic_severity {

crates/editor/src/editor.rs 🔗

@@ -384,6 +384,7 @@ pub struct EditorStyle {
     pub status: StatusColors,
     pub inlay_hints_style: HighlightStyle,
     pub suggestions_style: HighlightStyle,
+    pub unnecessary_code_fade: f32,
 }
 
 impl Default for EditorStyle {
@@ -400,6 +401,7 @@ impl Default for EditorStyle {
             status: StatusColors::dark(),
             inlay_hints_style: HighlightStyle::default(),
             suggestions_style: HighlightStyle::default(),
+            unnecessary_code_fade: Default::default(),
         }
     }
 }
@@ -9698,6 +9700,7 @@ impl Editor {
                                                     color: Some(cx.theme().status().predictive),
                                                     ..HighlightStyle::default()
                                                 },
+                                                ..EditorStyle::default()
                                             },
                                         ))
                                         .into_any_element()
@@ -12588,6 +12591,7 @@ impl Render for Editor {
                     color: Some(cx.theme().status().predictive),
                     ..HighlightStyle::default()
                 },
+                unnecessary_code_fade: ThemeSettings::get_global(cx).unnecessary_code_fade,
             },
         )
     }

crates/theme/src/settings.rs 🔗

@@ -91,6 +91,7 @@ pub struct ThemeSettings {
     pub active_theme: Arc<Theme>,
     pub theme_overrides: Option<ThemeStyleContent>,
     pub ui_density: UiDensity,
+    pub unnecessary_code_fade: f32,
 }
 
 impl ThemeSettings {
@@ -283,6 +284,10 @@ pub struct ThemeSettingsContent {
     #[serde(rename = "unstable.ui_density", default)]
     pub ui_density: Option<UiDensity>,
 
+    /// How much to fade out unused code.
+    #[serde(default)]
+    pub unnecessary_code_fade: Option<f32>,
+
     /// EXPERIMENTAL: Overrides for the current theme.
     ///
     /// These values will override the ones on the current theme specified in `theme`.
@@ -550,6 +555,7 @@ impl settings::Settings for ThemeSettings {
                 .unwrap(),
             theme_overrides: None,
             ui_density: defaults.ui_density.unwrap_or(UiDensity::Default),
+            unnecessary_code_fade: defaults.unnecessary_code_fade.unwrap_or(0.0),
         };
 
         for value in sources.user.into_iter().chain(sources.release_channel) {
@@ -602,6 +608,10 @@ impl settings::Settings for ThemeSettings {
                 value.buffer_font_size.map(Into::into),
             );
             merge(&mut this.buffer_line_height, value.buffer_line_height);
+
+            // Clamp the `unnecessary_code_fade` to ensure text can't disappear entirely.
+            merge(&mut this.unnecessary_code_fade, value.unnecessary_code_fade);
+            this.unnecessary_code_fade = this.unnecessary_code_fade.clamp(0.0, 0.9);
         }
 
         Ok(this)

docs/src/configuring-zed.md 🔗

@@ -1902,6 +1902,27 @@ Run the `theme selector: toggle` action in the command palette to see a current
 },
 ```
 
+## Unnecessary Code Fade
+
+- Description: How much to fade out unused code.
+- Setting: `unnecessary_code_fade`
+- Default: `0.3`
+
+**Options**
+
+Float values between `0.0` and `0.9`, where:
+
+- `0.0` means no fading (unused code looks the same as used code)
+- `0.9` means maximum fading (unused code is very faint but still visible)
+
+**Example**
+
+```json
+{
+  "unnecessary_code_fade": 0.5
+}
+```
+
 ## An example configuration:
 
 ```json