1mod highlight_map;
2mod theme_registry;
3
4use anyhow::Result;
5use gpui::{
6 color::Color,
7 elements::{ContainerStyle, LabelStyle},
8 fonts::TextStyle,
9};
10use serde::{Deserialize, Deserializer};
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(Debug, Default, 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 #[serde(deserialize_with = "deserialize_syntax_theme")]
27 pub syntax: Vec<(String, TextStyle)>,
28}
29
30#[derive(Debug, Default, Deserialize)]
31pub struct Workspace {
32 pub background: Color,
33 pub tab: Tab,
34 pub active_tab: Tab,
35 pub sidebar: ContainerStyle,
36 pub sidebar_icon: SidebarIcon,
37 pub active_sidebar_icon: SidebarIcon,
38}
39
40#[derive(Debug, Default, Deserialize)]
41pub struct Tab {
42 #[serde(flatten)]
43 pub container: ContainerStyle,
44 #[serde(flatten)]
45 pub label: LabelStyle,
46 pub icon_close: Color,
47 pub icon_dirty: Color,
48 pub icon_conflict: Color,
49}
50
51#[derive(Debug, Default, Deserialize)]
52pub struct SidebarIcon {
53 pub color: Color,
54}
55
56#[derive(Debug, Default, Deserialize)]
57pub struct ChatPanel {
58 pub message: ChatMessage,
59}
60
61#[derive(Debug, Default, Deserialize)]
62pub struct ChatMessage {
63 pub body: TextStyle,
64}
65
66#[derive(Debug, Default, Deserialize)]
67pub struct Selector {
68 #[serde(flatten)]
69 pub container: ContainerStyle,
70 #[serde(flatten)]
71 pub label: LabelStyle,
72
73 pub item: SelectorItem,
74 pub active_item: SelectorItem,
75}
76
77#[derive(Debug, Default, Deserialize)]
78pub struct SelectorItem {
79 #[serde(flatten)]
80 pub container: ContainerStyle,
81 #[serde(flatten)]
82 pub label: LabelStyle,
83}
84
85#[derive(Debug, Deserialize)]
86pub struct Editor {
87 pub background: Color,
88 pub gutter_background: Color,
89 pub active_line_background: Color,
90 pub line_number: Color,
91 pub line_number_active: Color,
92 pub text: Color,
93 pub replicas: Vec<Replica>,
94}
95
96#[derive(Clone, Copy, Debug, Default, Deserialize)]
97pub struct Replica {
98 pub cursor: Color,
99 pub selection: Color,
100}
101
102impl Theme {
103 pub fn highlight_style(&self, id: HighlightId) -> TextStyle {
104 self.syntax
105 .get(id.0 as usize)
106 .map(|entry| entry.1.clone())
107 .unwrap_or_else(|| TextStyle {
108 color: self.editor.text,
109 font_properties: Default::default(),
110 })
111 }
112
113 #[cfg(test)]
114 pub fn highlight_name(&self, id: HighlightId) -> Option<&str> {
115 self.syntax.get(id.0 as usize).map(|e| e.0.as_str())
116 }
117}
118
119impl Default for Editor {
120 fn default() -> Self {
121 Self {
122 background: Default::default(),
123 gutter_background: Default::default(),
124 active_line_background: Default::default(),
125 line_number: Default::default(),
126 line_number_active: Default::default(),
127 text: Default::default(),
128 replicas: vec![Replica::default()],
129 }
130 }
131}
132
133pub fn deserialize_syntax_theme<'de, D>(
134 deserializer: D,
135) -> Result<Vec<(String, TextStyle)>, D::Error>
136where
137 D: Deserializer<'de>,
138{
139 let mut result = Vec::<(String, TextStyle)>::new();
140
141 let syntax_data: HashMap<String, TextStyle> = Deserialize::deserialize(deserializer)?;
142 for (key, style) in syntax_data {
143 match result.binary_search_by(|(needle, _)| needle.cmp(&key)) {
144 Ok(i) | Err(i) => {
145 result.insert(i, (key, style));
146 }
147 }
148 }
149
150 Ok(result)
151}