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