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