theme.rs

  1mod highlight_map;
  2mod theme_registry;
  3
  4use anyhow::Result;
  5use gpui::{
  6    color::Color,
  7    elements::{ContainerStyle, LabelStyle},
  8    fonts::{HighlightStyle, TextStyle},
  9};
 10use serde::{de, Deserialize};
 11use std::collections::HashMap;
 12
 13pub use highlight_map::*;
 14pub use theme_registry::*;
 15
 16pub const DEFAULT_THEME_NAME: &'static str = "dark";
 17
 18#[derive(Deserialize)]
 19pub struct Theme {
 20    #[serde(default)]
 21    pub name: String,
 22    pub workspace: Workspace,
 23    pub chat_panel: ChatPanel,
 24    pub selector: Selector,
 25    pub editor: Editor,
 26    pub syntax: SyntaxTheme,
 27}
 28
 29pub struct SyntaxTheme {
 30    highlights: Vec<(String, HighlightStyle)>,
 31    default_style: HighlightStyle,
 32}
 33
 34#[derive(Deserialize)]
 35pub struct Workspace {
 36    pub background: Color,
 37    pub tab: Tab,
 38    pub active_tab: Tab,
 39    pub sidebar: Sidebar,
 40    pub sidebar_icon: SidebarIcon,
 41    pub active_sidebar_icon: SidebarIcon,
 42}
 43
 44#[derive(Deserialize)]
 45pub struct Tab {
 46    #[serde(flatten)]
 47    pub container: ContainerStyle,
 48    #[serde(flatten)]
 49    pub label: LabelStyle,
 50    pub icon_close: Color,
 51    pub icon_dirty: Color,
 52    pub icon_conflict: Color,
 53}
 54
 55#[derive(Deserialize)]
 56pub struct Sidebar {
 57    pub icons: ContainerStyle,
 58    pub resize_handle: ContainerStyle,
 59}
 60
 61#[derive(Deserialize)]
 62pub struct SidebarIcon {
 63    pub color: Color,
 64}
 65
 66#[derive(Deserialize)]
 67pub struct ChatPanel {
 68    #[serde(flatten)]
 69    pub container: ContainerStyle,
 70    pub message: ChatMessage,
 71    pub channel_name: TextStyle,
 72    pub channel_name_hash: ContainedLabel,
 73}
 74
 75#[derive(Deserialize)]
 76pub struct ChatMessage {
 77    pub body: TextStyle,
 78    pub sender: ContainedLabel,
 79    pub timestamp: ContainedLabel,
 80}
 81
 82#[derive(Deserialize)]
 83pub struct Selector {
 84    #[serde(flatten)]
 85    pub container: ContainerStyle,
 86    #[serde(flatten)]
 87    pub label: LabelStyle,
 88
 89    pub item: ContainedLabel,
 90    pub active_item: ContainedLabel,
 91}
 92
 93#[derive(Deserialize)]
 94pub struct ContainedLabel {
 95    #[serde(flatten)]
 96    pub container: ContainerStyle,
 97    #[serde(flatten)]
 98    pub label: LabelStyle,
 99}
100
101#[derive(Deserialize)]
102pub struct Editor {
103    pub background: Color,
104    pub gutter_background: Color,
105    pub active_line_background: Color,
106    pub line_number: Color,
107    pub line_number_active: Color,
108    pub replicas: Vec<Replica>,
109}
110
111#[derive(Clone, Copy, Deserialize)]
112pub struct Replica {
113    pub cursor: Color,
114    pub selection: Color,
115}
116
117impl SyntaxTheme {
118    pub fn new(default_style: HighlightStyle, highlights: Vec<(String, HighlightStyle)>) -> Self {
119        Self {
120            default_style,
121            highlights,
122        }
123    }
124
125    pub fn highlight_style(&self, id: HighlightId) -> HighlightStyle {
126        self.highlights
127            .get(id.0 as usize)
128            .map(|entry| entry.1.clone())
129            .unwrap_or_else(|| self.default_style.clone())
130    }
131
132    #[cfg(test)]
133    pub fn highlight_name(&self, id: HighlightId) -> Option<&str> {
134        self.highlights.get(id.0 as usize).map(|e| e.0.as_str())
135    }
136}
137
138impl<'de> Deserialize<'de> for SyntaxTheme {
139    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
140    where
141        D: serde::Deserializer<'de>,
142    {
143        let mut syntax_data: HashMap<String, HighlightStyle> =
144            Deserialize::deserialize(deserializer)?;
145
146        let mut result = Self {
147            highlights: Vec::<(String, HighlightStyle)>::new(),
148            default_style: syntax_data
149                .remove("default")
150                .ok_or_else(|| de::Error::custom("must specify a default color in syntax theme"))?,
151        };
152
153        for (key, style) in syntax_data {
154            match result
155                .highlights
156                .binary_search_by(|(needle, _)| needle.cmp(&key))
157            {
158                Ok(i) | Err(i) => {
159                    result.highlights.insert(i, (key, style));
160                }
161            }
162        }
163
164        Ok(result)
165    }
166}