1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
use serde::{Deserialize, Serialize};

use crate::Theme;

use super::{to_hex, OutputError};

/// Represents the workbench.colorCustomizations section of a VS Code settings.json file
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VsTheme {
    #[serde(rename = "editor.background")]
    editor_background: String,
    #[serde(rename = "sideBar.background")]
    sidebar_background: String,
    #[serde(rename = "activityBar.background")]
    activity_bar_background: String,
    #[serde(rename = "notificationCenterHeader.background")]
    notification_center_header_background: String,
    #[serde(rename = "notifications.background")]
    notifications_background: String,
    #[serde(rename = "activityBarTop.activeBackground")]
    activity_bar_top_active_background: String,
    #[serde(rename = "editorGroupHeader.tabsBackground")]
    editor_group_header_tabs_background: String,
    #[serde(rename = "editorGroupHeader.noTabsBackground")]
    editor_group_header_no_tabs_background: String,
    #[serde(rename = "titleBar.activeBackground")]
    title_bar_active_background: String,
    #[serde(rename = "titleBar.inactiveBackground")]
    title_bar_inactive_background: String,
    #[serde(rename = "statusBar.background")]
    status_bar_background: String,
    #[serde(rename = "statusBar.noFolderBackground")]
    status_bar_no_folder_background: String,
    #[serde(rename = "statusBar.debuggingBackground")]
    status_bar_debugging_background: String,
    #[serde(rename = "tab.activeBackground")]
    tab_active_background: String,
    #[serde(rename = "tab.activeBorder")]
    tab_active_border: String,
    #[serde(rename = "tab.activeBorderTop")]
    tab_active_border_top: String,
    #[serde(rename = "tab.hoverBackground")]
    tab_hover_background: String,
    #[serde(rename = "quickInput.background")]
    quick_input_background: String,
    #[serde(rename = "tab.inactiveBackground")]
    tab_inactive_background: String,
    #[serde(rename = "sideBarSectionHeader.background")]
    side_bar_section_header_background: String,
    #[serde(rename = "list.focusOutline")]
    list_focus_outline: String,
    #[serde(rename = "banner.background")]
    banner_background: String,
    #[serde(rename = "breadcrumb.background")]
    breadcrumb_background: String,
    #[serde(rename = "commandCenter.background")]
    command_center_background: String,
    #[serde(rename = "terminal.background")]
    terminal_background: String,
    #[serde(rename = "menu.background")]
    menu_background: String,
    #[serde(rename = "panel.background")]
    panel_background: String,
    #[serde(rename = "peekViewEditorGutter.background")]
    peek_view_editor_gutter_background: String,
    #[serde(rename = "peekViewResult.background")]
    peek_view_result_background: String,
    #[serde(rename = "peekViewTitle.background")]
    peek_view_title_background: String,
    #[serde(rename = "peekViewEditor.background")]
    peek_view_editor_background: String,
    #[serde(rename = "peekViewResult.selectionBackground")]
    peek_view_result_selection_background: String,
    #[serde(rename = "editorWidget.background")]
    editor_widget_background: String,
    #[serde(rename = "editorSuggestWidget.background")]
    editor_suggest_widget_background: String,
    #[serde(rename = "editorHoverWidget.background")]
    editor_hover_widget_background: String,
    #[serde(rename = "input.background")]
    input_background: String,
    #[serde(rename = "dropdown.background")]
    dropdown_background: String,
    #[serde(rename = "settings.checkboxBackground")]
    settings_checkbox_background: String,
    #[serde(rename = "settings.textInputBackground")]
    settings_text_input_background: String,
    #[serde(rename = "settings.numberInputBackground")]
    settings_number_input_background: String,
    #[serde(rename = "settings.dropdownBackground")]
    settings_dropdown_background: String,
    #[serde(rename = "sideBar.dropBackground")]
    side_bar_drop_background: String,
    #[serde(rename = "list.activeSelectionBackground")]
    list_active_selection_background: String,
    #[serde(rename = "list.inactiveSelectionBackground")]
    list_inactive_selection_background: String,
    #[serde(rename = "list.focusBackground")]
    list_focus_background: String,
    #[serde(rename = "list.hoverBackground")]
    list_hover_background: String,

    // text colors
    #[serde(rename = "editor.foreground")]
    editor_foreground: String,
    #[serde(rename = "editorLineNumber.foreground")]
    editor_line_number_foreground: String,
    #[serde(rename = "editorCursor.foreground")]
    editor_cursor_foreground: String,
    #[serde(rename = "sideBar.foreground")]
    side_bar_foreground: String,
    #[serde(rename = "activityBar.foreground")]
    activity_bar_foreground: String,
    #[serde(rename = "statusBar.foreground")]
    status_bar_foreground: String,
    #[serde(rename = "tab.activeForeground")]
    tab_active_foreground: String,
    #[serde(rename = "tab.inactiveForeground")]
    tab_inactive_foreground: String,
    #[serde(rename = "editorGroupHeader.tabsForeground")]
    editor_group_header_tabs_foreground: String,
    #[serde(rename = "sideBarSectionHeader.foreground")]
    side_bar_section_header_foreground: String,
    #[serde(rename = "statusBar.debuggingForeground")]
    status_bar_debugging_foreground: String,
    #[serde(rename = "statusBar.noFolderForeground")]
    status_bar_no_folder_foreground: String,
    #[serde(rename = "editorWidget.foreground")]
    editor_widget_foreground: String,
    #[serde(rename = "editorSuggestWidget.foreground")]
    editor_suggest_widget_foreground: String,
    #[serde(rename = "editorHoverWidget.foreground")]
    editor_hover_widget_foreground: String,
    #[serde(rename = "input.foreground")]
    input_foreground: String,
    #[serde(rename = "dropdown.foreground")]
    dropdown_foreground: String,
    #[serde(rename = "terminal.foreground")]
    terminal_foreground: String,
    #[serde(rename = "menu.foreground")]
    menu_foreground: String,
    #[serde(rename = "panel.foreground")]
    panel_foreground: String,
    #[serde(rename = "peekViewEditorGutter.foreground")]
    peek_view_editor_gutter_foreground: String,
    #[serde(rename = "peekViewResult.selectionForeground")]
    peek_view_result_selection_foreground: String,
    #[serde(rename = "inputOption.activeBorder")]
    input_option_active_border: String,

    // accent colors
    #[serde(rename = "activityBarBadge.background")]
    activity_bar_badge_background: String,
    #[serde(rename = "statusBar.debuggingBorder")]
    status_bar_debugging_border: String,
    #[serde(rename = "button.background")]
    button_background: String,
    #[serde(rename = "button.hoverBackground")]
    button_hover_background: String,
    #[serde(rename = "statusBarItem.remoteBackground")]
    status_bar_item_remote_background: String,

    // accent fg colors
    #[serde(rename = "activityBarBadge.foreground")]
    activity_bar_badge_foreground: String,
    #[serde(rename = "button.foreground")]
    button_foreground: String,
    #[serde(rename = "textLink.foreground")]
    text_link_foreground: String,
    #[serde(rename = "textLink.activeForeground")]
    text_link_active_foreground: String,
    #[serde(rename = "peekView.border")]
    peek_view_border: String,
    #[serde(rename = "settings.checkboxForeground")]
    settings_checkbox_foreground: String,
}

impl From<Theme> for VsTheme {
    fn from(theme: Theme) -> Self {
        Self {
            editor_background: format!("#{}", to_hex(theme.background.base)),
            sidebar_background: format!("#{}", to_hex(theme.primary.base)),
            activity_bar_background: format!("#{}", to_hex(theme.primary.base)),
            notification_center_header_background: format!("#{}", to_hex(theme.background.base)),
            notifications_background: format!("#{}", to_hex(theme.background.base)),
            activity_bar_top_active_background: format!("#{}", to_hex(theme.primary.base)),
            editor_group_header_tabs_background: format!("#{}", to_hex(theme.background.base)),
            editor_group_header_no_tabs_background: format!("#{}", to_hex(theme.background.base)),
            title_bar_active_background: format!("#{}", to_hex(theme.background.component.base)),
            title_bar_inactive_background: format!(
                "#{}",
                to_hex(theme.background.component.disabled)
            ),
            status_bar_background: format!("#{}", to_hex(theme.background.base)),
            status_bar_no_folder_background: format!("#{}", to_hex(theme.background.base)),
            status_bar_debugging_background: format!("#{}", to_hex(theme.background.base)),
            tab_active_background: format!("#{}", to_hex(theme.primary.component.pressed)),
            tab_active_border: format!("#{}", to_hex(theme.accent.base)),
            tab_active_border_top: format!("#{}", to_hex(theme.accent.base)),
            tab_hover_background: format!("#{}", to_hex(theme.primary.component.hover)),
            tab_inactive_background: format!("#{}", to_hex(theme.primary.component.base)),
            quick_input_background: format!("#{}", to_hex(theme.primary.base)),
            side_bar_section_header_background: format!("#{}", to_hex(theme.primary.base)),
            banner_background: format!("#{}", to_hex(theme.primary.base)),
            breadcrumb_background: format!("#{}", to_hex(theme.primary.base)),
            command_center_background: format!("#{}", to_hex(theme.primary.base)),
            terminal_background: format!("#{}", to_hex(theme.primary.base)),
            menu_background: format!("#{}", to_hex(theme.primary.base)),
            panel_background: format!("#{}", to_hex(theme.primary.base)),
            peek_view_editor_gutter_background: format!("#{}", to_hex(theme.background.base)),
            peek_view_result_background: format!("#{}", to_hex(theme.background.base)),
            peek_view_title_background: format!("#{}", to_hex(theme.background.base)),
            peek_view_editor_background: format!("#{}", to_hex(theme.background.base)),
            peek_view_result_selection_background: format!("#{}", to_hex(theme.background.base)),
            editor_widget_background: format!("#{}", to_hex(theme.background.base)),
            editor_suggest_widget_background: format!("#{}", to_hex(theme.background.base)),
            editor_hover_widget_background: format!("#{}", to_hex(theme.background.base)),
            input_background: format!("#{}", to_hex(theme.background.base)),
            dropdown_background: format!("#{}", to_hex(theme.background.base)),
            settings_checkbox_background: format!("#{}", to_hex(theme.background.base)),
            settings_text_input_background: format!("#{}", to_hex(theme.background.base)),
            settings_number_input_background: format!("#{}", to_hex(theme.background.base)),
            settings_dropdown_background: format!("#{}", to_hex(theme.background.base)),
            side_bar_drop_background: format!("#{}", to_hex(theme.background.base)),
            list_active_selection_background: format!("#{}", to_hex(theme.primary.base)),
            list_inactive_selection_background: format!("#{}", to_hex(theme.primary.base)),
            list_focus_background: format!("#{}", to_hex(theme.primary.base)),
            list_hover_background: format!("#{}", to_hex(theme.primary.base)),
            editor_foreground: format!("#{}", to_hex(theme.background.on)),
            editor_line_number_foreground: format!("#{}", to_hex(theme.background.on)),
            editor_cursor_foreground: format!("#{}", to_hex(theme.background.on)),
            side_bar_foreground: format!("#{}", to_hex(theme.primary.on)),
            activity_bar_foreground: format!("#{}", to_hex(theme.primary.on)),
            status_bar_foreground: format!("#{}", to_hex(theme.primary.on)),
            tab_active_foreground: format!("#{}", to_hex(theme.primary.on)),
            tab_inactive_foreground: format!("#{}", to_hex(theme.primary.on)),
            editor_group_header_tabs_foreground: format!("#{}", to_hex(theme.primary.on)),
            side_bar_section_header_foreground: format!("#{}", to_hex(theme.primary.on)),
            status_bar_debugging_foreground: format!("#{}", to_hex(theme.primary.on)),
            status_bar_no_folder_foreground: format!("#{}", to_hex(theme.primary.on)),
            editor_widget_foreground: format!("#{}", to_hex(theme.primary.on)),
            editor_suggest_widget_foreground: format!("#{}", to_hex(theme.primary.on)),
            editor_hover_widget_foreground: format!("#{}", to_hex(theme.primary.on)),
            input_foreground: format!("#{}", to_hex(theme.primary.on)),
            dropdown_foreground: format!("#{}", to_hex(theme.primary.on)),
            terminal_foreground: format!("#{}", to_hex(theme.primary.on)),
            menu_foreground: format!("#{}", to_hex(theme.primary.on)),
            panel_foreground: format!("#{}", to_hex(theme.primary.on)),
            peek_view_editor_gutter_foreground: format!("#{}", to_hex(theme.primary.on)),
            peek_view_result_selection_foreground: format!("#{}", to_hex(theme.primary.on)),
            input_option_active_border: format!("#{}", to_hex(theme.accent.base)),
            activity_bar_badge_background: format!("#{}", to_hex(theme.accent.base)),
            activity_bar_badge_foreground: format!("#{}", to_hex(theme.accent.on)),
            status_bar_debugging_border: format!("#{}", to_hex(theme.accent.base)),
            list_focus_outline: format!("#{}", to_hex(theme.accent.base)),
            button_background: format!("#{}", to_hex(theme.accent_button.base)),
            button_hover_background: format!("#{}", to_hex(theme.accent_button.hover)),
            status_bar_item_remote_background: format!("#{}", to_hex(theme.accent.base)),
            button_foreground: format!("#{}", to_hex(theme.accent_button.on)),
            text_link_foreground: format!("#{}", to_hex(theme.accent.base)),
            text_link_active_foreground: format!("#{}", to_hex(theme.accent.base)),
            peek_view_border: format!("#{}", to_hex(theme.accent.base)),
            settings_checkbox_foreground: format!("#{}", to_hex(theme.accent.base)),
        }
    }
}

impl Theme {
    pub fn apply_vs_code(self) -> Result<(), OutputError> {
        let vs_theme = VsTheme::from(self);
        let config_dir = dirs::config_dir().ok_or(OutputError::MissingConfigDir)?;
        let vs_code_dir = config_dir.join("Code").join("User");
        if !vs_code_dir.exists() {
            std::fs::create_dir_all(&vs_code_dir).map_err(OutputError::Io)?;
        }

        // just add the json entry for workbench.colorCustomizations
        let settings_file = vs_code_dir.join("settings.json");
        let settings = std::fs::read_to_string(&settings_file).unwrap_or_default();
        let mut settings: serde_json::Value = serde_json::from_str(&settings)?;
        settings["workbench.colorCustomizations"] = serde_json::to_value(vs_theme).unwrap();
        settings["window.autoDetectColorScheme"] = serde_json::Value::Bool(true);
        std::fs::write(
            &settings_file,
            serde_json::to_string_pretty(&settings).unwrap(),
        )
        .map_err(OutputError::Io)?;

        Ok(())
    }

    pub fn reset_vs_code() -> Result<(), OutputError> {
        let config_dir = dirs::config_dir().ok_or(OutputError::MissingConfigDir)?;
        let vs_code_dir = config_dir.join("Code").join("User");
        let settings_file = vs_code_dir.join("settings.json");
        // just remove the json entry for workbench.colorCustomizations
        let settings = std::fs::read_to_string(&settings_file).unwrap_or_default();
        let mut settings: serde_json::Value = serde_json::from_str(&settings).unwrap_or_default();
        settings["workbench.colorCustomizations"] = serde_json::Value::Null;

        std::fs::write(
            &settings_file,
            serde_json::to_string_pretty(&settings).unwrap(),
        )
        .map_err(OutputError::Io)?;

        Ok(())
    }
}