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 #[serde(flatten)]
59 pub container: ContainerStyle,
60 pub message: ChatMessage,
61}
62
63#[derive(Debug, Default, Deserialize)]
64pub struct ChatMessage {
65 pub body: TextStyle,
66 pub sender: ContainedLabel,
67 pub timestamp: ContainedLabel,
68}
69
70#[derive(Debug, Default, Deserialize)]
71pub struct Selector {
72 #[serde(flatten)]
73 pub container: ContainerStyle,
74 #[serde(flatten)]
75 pub label: LabelStyle,
76
77 pub item: ContainedLabel,
78 pub active_item: ContainedLabel,
79}
80
81#[derive(Debug, Default, Deserialize)]
82pub struct ContainedLabel {
83 #[serde(flatten)]
84 pub container: ContainerStyle,
85 #[serde(flatten)]
86 pub label: LabelStyle,
87}
88
89#[derive(Debug, Deserialize)]
90pub struct Editor {
91 pub background: Color,
92 pub gutter_background: Color,
93 pub active_line_background: Color,
94 pub line_number: Color,
95 pub line_number_active: Color,
96 pub text: Color,
97 pub replicas: Vec<Replica>,
98}
99
100#[derive(Clone, Copy, Debug, Default, Deserialize)]
101pub struct Replica {
102 pub cursor: Color,
103 pub selection: Color,
104}
105
106impl Theme {
107 pub fn highlight_style(&self, id: HighlightId) -> TextStyle {
108 self.syntax
109 .get(id.0 as usize)
110 .map(|entry| entry.1.clone())
111 .unwrap_or_else(|| TextStyle {
112 color: self.editor.text,
113 font_properties: Default::default(),
114 })
115 }
116
117 #[cfg(test)]
118 pub fn highlight_name(&self, id: HighlightId) -> Option<&str> {
119 self.syntax.get(id.0 as usize).map(|e| e.0.as_str())
120 }
121}
122
123impl Default for Editor {
124 fn default() -> Self {
125 Self {
126 background: Default::default(),
127 gutter_background: Default::default(),
128 active_line_background: Default::default(),
129 line_number: Default::default(),
130 line_number_active: Default::default(),
131 text: Default::default(),
132 replicas: vec![Replica::default()],
133 }
134 }
135}
136
137pub fn deserialize_syntax_theme<'de, D>(
138 deserializer: D,
139) -> Result<Vec<(String, TextStyle)>, D::Error>
140where
141 D: Deserializer<'de>,
142{
143 let mut result = Vec::<(String, TextStyle)>::new();
144
145 let syntax_data: HashMap<String, TextStyle> = Deserialize::deserialize(deserializer)?;
146 for (key, style) in syntax_data {
147 match result.binary_search_by(|(needle, _)| needle.cmp(&key)) {
148 Ok(i) | Err(i) => {
149 result.insert(i, (key, style));
150 }
151 }
152 }
153
154 Ok(result)
155}