Allow overriding player colors via `experimental.theme_overrides` (#8692)

Marshall Bowers created

This PR extends the `experimental.theme_overrides` to allow overriding
the player colors.

Release Notes:

- Added the ability to override player colors using
`experimenta.theme_overrides`.

Change summary

crates/theme/src/registry.rs       | 36 ++--------------------------
crates/theme/src/settings.rs       |  1 
crates/theme/src/styles/players.rs | 40 +++++++++++++++++++++++++++++++
3 files changed, 43 insertions(+), 34 deletions(-)

Detailed changes

crates/theme/src/registry.rs 🔗

@@ -12,9 +12,8 @@ use refineable::Refineable;
 use util::ResultExt;
 
 use crate::{
-    try_parse_color, Appearance, AppearanceContent, PlayerColor, PlayerColors, StatusColors,
-    SyntaxTheme, SystemColors, Theme, ThemeColors, ThemeContent, ThemeFamily, ThemeFamilyContent,
-    ThemeStyles,
+    try_parse_color, Appearance, AppearanceContent, PlayerColors, StatusColors, SyntaxTheme,
+    SystemColors, Theme, ThemeColors, ThemeContent, ThemeFamily, ThemeFamilyContent, ThemeStyles,
 };
 
 #[derive(Debug, Clone)]
@@ -117,36 +116,7 @@ impl ThemeRegistry {
                 AppearanceContent::Light => PlayerColors::light(),
                 AppearanceContent::Dark => PlayerColors::dark(),
             };
-            if !user_theme.style.players.is_empty() {
-                for (idx, player) in user_theme.style.players.clone().into_iter().enumerate() {
-                    let cursor = player
-                        .cursor
-                        .as_ref()
-                        .and_then(|color| try_parse_color(&color).ok());
-                    let background = player
-                        .background
-                        .as_ref()
-                        .and_then(|color| try_parse_color(&color).ok());
-                    let selection = player
-                        .selection
-                        .as_ref()
-                        .and_then(|color| try_parse_color(&color).ok());
-
-                    if let Some(player_color) = player_colors.0.get_mut(idx) {
-                        *player_color = PlayerColor {
-                            cursor: cursor.unwrap_or(player_color.cursor),
-                            background: background.unwrap_or(player_color.background),
-                            selection: selection.unwrap_or(player_color.selection),
-                        };
-                    } else {
-                        player_colors.0.push(PlayerColor {
-                            cursor: cursor.unwrap_or_default(),
-                            background: background.unwrap_or_default(),
-                            selection: selection.unwrap_or_default(),
-                        });
-                    }
-                }
-            }
+            player_colors.merge(&user_theme.style.players);
 
             let mut syntax_colors = match user_theme.appearance {
                 AppearanceContent::Light => SyntaxTheme::light(),

crates/theme/src/settings.rs 🔗

@@ -243,6 +243,7 @@ impl ThemeSettings {
                 .styles
                 .status
                 .refine(&theme_overrides.status_colors_refinement());
+            base_theme.styles.player.merge(&theme_overrides.players);
             base_theme.styles.syntax = Arc::new(SyntaxTheme {
                 highlights: {
                     let mut highlights = base_theme.styles.syntax.highlights.clone();

crates/theme/src/styles/players.rs 🔗

@@ -1,7 +1,9 @@
 use gpui::Hsla;
 use serde_derive::Deserialize;
 
-use crate::{amber, blue, jade, lime, orange, pink, purple, red};
+use crate::{
+    amber, blue, jade, lime, orange, pink, purple, red, try_parse_color, PlayerColorContent,
+};
 
 #[derive(Debug, Clone, Copy, Deserialize, Default)]
 pub struct PlayerColor {
@@ -142,4 +144,40 @@ impl PlayerColors {
         let len = self.0.len() - 1;
         self.0[(participant_index as usize % len) + 1]
     }
+
+    /// Merges the given player colors into this [`PlayerColors`] instance.
+    pub fn merge(&mut self, user_player_colors: &[PlayerColorContent]) {
+        if user_player_colors.is_empty() {
+            return;
+        }
+
+        for (idx, player) in user_player_colors.into_iter().enumerate() {
+            let cursor = player
+                .cursor
+                .as_ref()
+                .and_then(|color| try_parse_color(&color).ok());
+            let background = player
+                .background
+                .as_ref()
+                .and_then(|color| try_parse_color(&color).ok());
+            let selection = player
+                .selection
+                .as_ref()
+                .and_then(|color| try_parse_color(&color).ok());
+
+            if let Some(player_color) = self.0.get_mut(idx) {
+                *player_color = PlayerColor {
+                    cursor: cursor.unwrap_or(player_color.cursor),
+                    background: background.unwrap_or(player_color.background),
+                    selection: selection.unwrap_or(player_color.selection),
+                };
+            } else {
+                self.0.push(PlayerColor {
+                    cursor: cursor.unwrap_or_default(),
+                    background: background.unwrap_or_default(),
+                    selection: selection.unwrap_or_default(),
+                });
+            }
+        }
+    }
 }