Flip the dependency between editor and theme

Antonio Scandurra , Nathan Sobo , and Max Brunsfeld created

Co-Authored-By: Nathan Sobo <nathan@zed.dev>
Co-Authored-By: Max Brunsfeld <max@zed.dev>

Change summary

Cargo.lock                         |  3 
crates/buffer/Cargo.toml           |  2 
crates/buffer/src/highlight_map.rs | 23 +++++++++-
crates/buffer/src/language.rs      |  3 
crates/buffer/src/lib.rs           |  2 
crates/buffer/src/syntax_theme.rs  | 50 ------------------------
crates/editor/Cargo.toml           |  1 
crates/editor/src/display_map.rs   |  5 +
crates/editor/src/element.rs       |  5 -
crates/editor/src/lib.rs           | 28 -------------
crates/theme/Cargo.toml            |  1 
crates/theme/src/lib.rs            | 65 +++++++++++++++++++++++++++++++
12 files changed, 94 insertions(+), 94 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -760,6 +760,7 @@ dependencies = [
  "similar",
  "smallvec",
  "sum_tree",
+ "theme",
  "tree-sitter",
  "tree-sitter-rust",
  "unindent",
@@ -1631,6 +1632,7 @@ dependencies = [
  "smallvec",
  "smol",
  "sum_tree",
+ "theme",
  "tree-sitter",
  "tree-sitter-rust",
  "unindent",
@@ -5316,7 +5318,6 @@ name = "theme"
 version = "0.1.0"
 dependencies = [
  "anyhow",
- "editor",
  "gpui",
  "indexmap",
  "parking_lot",

crates/buffer/Cargo.toml 🔗

@@ -11,7 +11,7 @@ clock = { path = "../clock" }
 gpui = { path = "../gpui" }
 rpc = { path = "../rpc" }
 sum_tree = { path = "../sum_tree" }
-
+theme = { path = "../theme" }
 anyhow = "1.0.38"
 arrayvec = "0.7.1"
 lazy_static = "1.4"

crates/buffer/src/highlight_map.rs 🔗

@@ -1,5 +1,6 @@
-use crate::syntax_theme::SyntaxTheme;
+use gpui::fonts::HighlightStyle;
 use std::sync::Arc;
+use theme::SyntaxTheme;
 
 #[derive(Clone, Debug)]
 pub struct HighlightMap(Arc<[HighlightId]>);
@@ -49,6 +50,20 @@ impl HighlightMap {
     }
 }
 
+impl HighlightId {
+    pub fn style(&self, theme: &SyntaxTheme) -> Option<HighlightStyle> {
+        theme
+            .highlights
+            .get(self.0 as usize)
+            .map(|entry| entry.1.clone())
+    }
+
+    #[cfg(any(test, feature = "test-support"))]
+    pub fn name<'a>(&self, theme: &'a SyntaxTheme) -> Option<&'a str> {
+        theme.highlights.get(self.0 as usize).map(|e| e.0.as_str())
+    }
+}
+
 impl Default for HighlightMap {
     fn default() -> Self {
         Self(Arc::new([]))
@@ -89,8 +104,8 @@ mod tests {
         ];
 
         let map = HighlightMap::new(capture_names, &theme);
-        assert_eq!(theme.highlight_name(map.get(0)), Some("function"));
-        assert_eq!(theme.highlight_name(map.get(1)), Some("function.async"));
-        assert_eq!(theme.highlight_name(map.get(2)), Some("variable.builtin"));
+        assert_eq!(map.get(0).name(&theme), Some("function"));
+        assert_eq!(map.get(1).name(&theme), Some("function.async"));
+        assert_eq!(map.get(2).name(&theme), Some("variable.builtin"));
     }
 }

crates/buffer/src/language.rs 🔗

@@ -1,9 +1,10 @@
-use crate::{HighlightMap, SyntaxTheme};
+use crate::{HighlightMap};
 use parking_lot::Mutex;
 use serde::Deserialize;
 use std::{path::Path, str, sync::Arc};
 use tree_sitter::{Language as Grammar, Query};
 pub use tree_sitter::{Parser, Tree};
+use theme::SyntaxTheme;
 
 #[derive(Default, Deserialize)]
 pub struct LanguageConfig {

crates/buffer/src/lib.rs 🔗

@@ -7,7 +7,6 @@ mod point;
 pub mod random_char_iter;
 pub mod rope;
 mod selection;
-mod syntax_theme;
 
 pub use anchor::*;
 use anyhow::{anyhow, Result};
@@ -42,7 +41,6 @@ use std::{
     time::{Duration, Instant, SystemTime, UNIX_EPOCH},
 };
 use sum_tree::{Bias, FilterCursor, SumTree};
-pub use syntax_theme::SyntaxTheme;
 use tree_sitter::{InputEdit, Parser, QueryCursor};
 
 pub trait File {

crates/buffer/src/syntax_theme.rs 🔗

@@ -1,50 +0,0 @@
-use std::collections::HashMap;
-
-use crate::HighlightId;
-use gpui::fonts::HighlightStyle;
-use serde::Deserialize;
-
-#[derive(Default)]
-pub struct SyntaxTheme {
-    pub(crate) highlights: Vec<(String, HighlightStyle)>,
-}
-
-impl SyntaxTheme {
-    pub fn new(highlights: Vec<(String, HighlightStyle)>) -> Self {
-        Self { highlights }
-    }
-
-    pub fn highlight_style(&self, id: HighlightId) -> Option<HighlightStyle> {
-        self.highlights
-            .get(id.0 as usize)
-            .map(|entry| entry.1.clone())
-    }
-
-    #[cfg(any(test, feature = "test-support"))]
-    pub fn highlight_name(&self, id: HighlightId) -> Option<&str> {
-        self.highlights.get(id.0 as usize).map(|e| e.0.as_str())
-    }
-}
-
-impl<'de> Deserialize<'de> for SyntaxTheme {
-    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
-    where
-        D: serde::Deserializer<'de>,
-    {
-        let syntax_data: HashMap<String, HighlightStyle> = Deserialize::deserialize(deserializer)?;
-
-        let mut result = Self::new(Vec::new());
-        for (key, style) in syntax_data {
-            match result
-                .highlights
-                .binary_search_by(|(needle, _)| needle.cmp(&key))
-            {
-                Ok(i) | Err(i) => {
-                    result.highlights.insert(i, (key, style));
-                }
-            }
-        }
-
-        Ok(result)
-    }
-}

crates/editor/Cargo.toml 🔗

@@ -11,6 +11,7 @@ buffer = { path = "../buffer" }
 clock = { path = "../clock" }
 gpui = { path = "../gpui" }
 sum_tree = { path = "../sum_tree" }
+theme = { path = "../theme" }
 util = { path = "../util" }
 anyhow = "1.0"
 lazy_static = "1.4"

crates/editor/src/display_map.rs 🔗

@@ -358,10 +358,11 @@ impl ToDisplayPoint for Anchor {
 mod tests {
     use super::*;
     use crate::{movement, test::*};
-    use buffer::{History, Language, LanguageConfig, RandomCharIter, SelectionGoal, SyntaxTheme};
+    use buffer::{History, Language, LanguageConfig, RandomCharIter, SelectionGoal};
     use gpui::{color::Color, MutableAppContext};
     use rand::{prelude::StdRng, Rng};
     use std::{env, sync::Arc};
+    use theme::SyntaxTheme;
     use Bias::*;
 
     #[gpui::test(iterations = 100)]
@@ -976,7 +977,7 @@ mod tests {
         let mut snapshot = map.update(cx, |map, cx| map.snapshot(cx));
         let mut chunks: Vec<(String, Option<&str>)> = Vec::new();
         for (chunk, style_id) in snapshot.highlighted_chunks_for_rows(rows) {
-            let style_name = theme.highlight_name(style_id);
+            let style_name = style_id.name(theme);
             if let Some((last_chunk, last_style_name)) = chunks.last_mut() {
                 if style_name == *last_style_name {
                     last_chunk.push_str(chunk);

crates/editor/src/element.rs 🔗

@@ -513,9 +513,8 @@ impl EditorElement {
                 }
 
                 if !line_chunk.is_empty() && !line_exceeded_max_len {
-                    let highlight_style = style
-                        .syntax
-                        .highlight_style(style_ix)
+                    let highlight_style = style_ix
+                        .style(&style.syntax)
                         .unwrap_or(style.text.clone().into());
                     // Avoid a lookup if the font properties match the previous ones.
                     let font_id = if highlight_style.font_properties == prev_font_properties {

crates/editor/src/lib.rs 🔗

@@ -28,6 +28,7 @@ use std::{
     time::Duration,
 };
 use sum_tree::Bias;
+use theme::EditorStyle;
 use util::post_inc;
 
 const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
@@ -283,27 +284,6 @@ pub struct EditorSettings {
     pub style: EditorStyle,
 }
 
-#[derive(Clone, Deserialize, Default)]
-pub struct EditorStyle {
-    pub text: TextStyle,
-    #[serde(default)]
-    pub placeholder_text: Option<TextStyle>,
-    pub background: Color,
-    pub selection: SelectionStyle,
-    pub gutter_background: Color,
-    pub active_line_background: Color,
-    pub line_number: Color,
-    pub line_number_active: Color,
-    pub guest_selections: Vec<SelectionStyle>,
-    pub syntax: Arc<SyntaxTheme>,
-}
-
-#[derive(Clone, Copy, Default, Deserialize)]
-pub struct SelectionStyle {
-    pub cursor: Color,
-    pub selection: Color,
-}
-
 pub struct Editor {
     handle: WeakViewHandle<Self>,
     buffer: ModelHandle<Buffer>,
@@ -2448,12 +2428,6 @@ impl Snapshot {
     }
 }
 
-impl EditorStyle {
-    fn placeholder_text(&self) -> &TextStyle {
-        self.placeholder_text.as_ref().unwrap_or(&self.text)
-    }
-}
-
 impl EditorSettings {
     #[cfg(any(test, feature = "test-support"))]
     pub fn test(cx: &AppContext) -> Self {

crates/theme/Cargo.toml 🔗

@@ -4,7 +4,6 @@ version = "0.1.0"
 edition = "2018"
 
 [dependencies]
-editor = { path = "../editor" }
 gpui = { path = "../gpui" }
 anyhow = "1.0.38"
 indexmap = "1.6.2"

crates/theme/src/lib.rs 🔗

@@ -1,14 +1,14 @@
 mod resolution;
 mod theme_registry;
 
-use editor::{EditorStyle, SelectionStyle};
 use gpui::{
     color::Color,
     elements::{ContainerStyle, ImageStyle, LabelStyle},
-    fonts::TextStyle,
+    fonts::{HighlightStyle, TextStyle},
     Border,
 };
 use serde::Deserialize;
+use std::{collections::HashMap, sync::Arc};
 
 pub use theme_registry::*;
 
@@ -201,6 +201,27 @@ pub struct ContainedLabel {
     pub label: LabelStyle,
 }
 
+#[derive(Clone, Deserialize, Default)]
+pub struct EditorStyle {
+    pub text: TextStyle,
+    #[serde(default)]
+    pub placeholder_text: Option<TextStyle>,
+    pub background: Color,
+    pub selection: SelectionStyle,
+    pub gutter_background: Color,
+    pub active_line_background: Color,
+    pub line_number: Color,
+    pub line_number_active: Color,
+    pub guest_selections: Vec<SelectionStyle>,
+    pub syntax: Arc<SyntaxTheme>,
+}
+
+#[derive(Clone, Copy, Default, Deserialize)]
+pub struct SelectionStyle {
+    pub cursor: Color,
+    pub selection: Color,
+}
+
 #[derive(Clone, Deserialize, Default)]
 pub struct InputEditorStyle {
     #[serde(flatten)]
@@ -211,6 +232,12 @@ pub struct InputEditorStyle {
     pub selection: SelectionStyle,
 }
 
+impl EditorStyle {
+    pub fn placeholder_text(&self) -> &TextStyle {
+        self.placeholder_text.as_ref().unwrap_or(&self.text)
+    }
+}
+
 impl InputEditorStyle {
     pub fn as_editor(&self) -> EditorStyle {
         EditorStyle {
@@ -230,3 +257,37 @@ impl InputEditorStyle {
         }
     }
 }
+
+#[derive(Default)]
+pub struct SyntaxTheme {
+    pub highlights: Vec<(String, HighlightStyle)>,
+}
+
+impl SyntaxTheme {
+    pub fn new(highlights: Vec<(String, HighlightStyle)>) -> Self {
+        Self { highlights }
+    }
+}
+
+impl<'de> Deserialize<'de> for SyntaxTheme {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: serde::Deserializer<'de>,
+    {
+        let syntax_data: HashMap<String, HighlightStyle> = Deserialize::deserialize(deserializer)?;
+
+        let mut result = Self::new(Vec::new());
+        for (key, style) in syntax_data {
+            match result
+                .highlights
+                .binary_search_by(|(needle, _)| needle.cmp(&key))
+            {
+                Ok(i) | Err(i) => {
+                    result.highlights.insert(i, (key, style));
+                }
+            }
+        }
+
+        Ok(result)
+    }
+}