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}