duat_catppuccin/
lib.rs

1//! # Duat Catppuccin
2//!
3//! This is an implementation of the [Catppuccin](https://catppuccin.com)
4//! colorschemes for Duat.
5//!
6//! When you `plug` this plugin, four colorschemes will be added to
7//! Duat:
8//!
9//! * `catppuccin-latte`;
10//! * `catppuccin-`frappe`;
11//! * `catppuccin-macchiato`;
12//! * `catppuccin-mocha`;
13//!
14//! This plugin lets you use its colors to modify other `Form`s with
15//! the `Catppuccin::modify` function. It also has a `no_background`
16//! function, if you don't want the background to change.
17//!
18//! # Installation
19//!
20//! Just like other Duat plugins, this one can be installed by calling
21//! `cargo add` in the config directory:
22//!
23//! ```bash
24//! cargo add duat-catppuccin@"*" --rename catppuccin
25//! ```
26//!
27//! Or, if you are using a `--git-deps` version of duat, do this:
28//!
29//! ```bash
30//! cargo add --git https://github.com/AhoyISki/duat-catppuccin --rename catppuccin
31//! ```
32use duat::prelude::*;
33
34pub struct Catppuccin {
35    no_background: bool,
36    modifications: Box<dyn Fn(Colors) + Send + Sync + 'static>,
37}
38
39impl Catppuccin {
40    /// Returns a new instance of the [`Catppuccin`] [`Plugin`]
41    pub fn new() -> Self {
42        Self {
43            no_background: false,
44            modifications: Box::new(|_| {}),
45        }
46    }
47}
48
49impl duat::prelude::Plugin for Catppuccin {
50    /// Adds the catppuccin colorschemes
51    ///
52    /// This will add the Latte, Frappe, Macchiato, and Mocha flavors,
53    /// modified by the options passed to [`Catppuccin`]
54    fn plug(self, _: &Plugins) {
55        let no_bg = self.no_background;
56        let m = Box::leak(self.modifications);
57        form::add_colorscheme(ColorScheme::latte(m).no_bg(no_bg));
58        form::add_colorscheme(ColorScheme::frappe(m).no_bg(no_bg));
59        form::add_colorscheme(ColorScheme::macchiato(m).no_bg(no_bg));
60        form::add_colorscheme(ColorScheme::mocha(m).no_bg(no_bg));
61    }
62}
63
64impl Catppuccin {
65    /// Disables the background color
66    ///
67    /// This can allow you to have, for example, a transparent
68    /// terminal.
69    pub fn no_background(self) -> Self {
70        Self { no_background: true, ..self }
71    }
72
73    /// Lets you modify forms, based on the chosen colorscheme
74    ///
75    /// For example, if you want red delimiters, you can do this:
76    ///
77    /// ```rust
78    /// use duat::prelude::*;
79    /// setup_duat!(setup);
80    ///
81    /// fn setup() {
82    ///     plug(duat_catppuccin::Catppuccin::new().modify(|colors| {
83    ///         form::set("punctuation.delimiter", colors.red);
84    ///     }));
85    /// }
86    /// ```
87    pub fn modify(self, modifications: impl Fn(Colors) + Send + Sync + 'static) -> Self {
88        let modifications = Box::new(move |c| {
89            modifications(c);
90        });
91        Self { modifications, ..self }
92    }
93}
94
95impl Default for Catppuccin {
96    fn default() -> Self {
97        Self::new()
98    }
99}
100
101#[derive(Default)]
102enum Flavour {
103    Latte,
104    Frappe,
105    Macchiato,
106    #[default]
107    Mocha,
108}
109
110struct ColorScheme {
111    flavour: Flavour,
112    no_background: bool,
113    modifications: &'static (dyn Fn(Colors) + Send + Sync),
114}
115
116impl form::ColorScheme for ColorScheme {
117    fn apply(&self) {
118        let c = match self.flavour {
119            Flavour::Latte => LATTE,
120            Flavour::Frappe => FRAPPE,
121            Flavour::Macchiato => MACCHIATO,
122            Flavour::Mocha => MOCHA,
123        };
124
125        if self.no_background {
126            form::set("default", Form::with(c.text));
127        } else {
128            form::set("default", Form::with(c.text).on(c.base));
129        }
130
131        form::set_many!(
132            // Base Duat Forms
133            ("accent", Form::with(c.rosewater).bold()),
134            ("default.error", Form::with(c.maroon)),
135            ("accent.error", Form::with(c.red).bold()),
136            ("default.warn", Form::with(c.yellow)),
137            ("accent.warn", Form::with(c.peach).bold()),
138            ("default.info", Form::with(c.sapphire)),
139            ("accent.info", Form::with(c.sky).bold()),
140            ("default.debug", Form::with(c.subtext1)),
141            ("accent.debug", Form::with(c.lavender).bold()),
142            ("caret.main", Form::reverse()),
143            ("caret.extra", Form::reverse()),
144            ("selection.main", Form::with(c.base).on(c.overlay1)),
145            ("selection.extra", Form::with(c.base).on(c.overlay0)),
146            ("cloak", Form::with(c.overlay1).on(c.base)),
147            ("character.control", Form::with(c.overlay1)),
148            // duat-base forms
149            ("linenum.main", Form::with(c.yellow)),
150            ("linenum.wrapped", Form::with(c.teal)),
151            ("file", Form::with(c.yellow)),
152            ("selections", Form::with(c.blue)),
153            ("coord", Form::with(c.peach)),
154            ("separator", "punctuation.delimiter"),
155            ("mode", Form::with(c.green)),
156            ("terminal.border", Form::with(c.surface0).on(c.base)),
157            ("terminal.frame", Form::with(c.text).on(c.base)),
158            ("notifs.colon", Form::with(c.subtext0)),
159            ("prompt", Form::with(c.green)),
160            ("prompt.colon", Form::with(c.subtext0)),
161            ("default.StatusLine", Form::on(c.surface0)),
162            ("default.LogBook", Form::on(c.surface0)),
163            ("default.VertRule", Form::with(c.surface0)),
164            ("default.LineNumbers", Form::with(c.overlay0)),
165            ("matched_pair", Form::with(c.peach).on(c.surface1).bold()),
166            ("log_book.location", Form::with(c.subtext1)),
167            ("default.Completions", Form::on(c.surface1)),
168            ("selected.Completions", Form::with(c.base).on(c.overlay0)),
169            ("default.WhichKey", Form::with(c.text)),
170            ("key", Form::with(c.peach)),
171            ("key.special", Form::with(c.teal)),
172            // For duatmode
173            ("caret.main.Normal", Form::with(c.base).on(c.text)),
174            ("caret.extra.Normal", Form::with(c.base).on(c.sapphire)),
175            ("caret.main.Insert", Form::with(c.base).on(c.mauve)),
176            ("caret.extra.Insert", Form::with(c.base).on(c.yellow)),
177            ("param", Form::with(c.lavender)),
178            ("param.flag", Form::with(c.pink)),
179            // Tree sitter Forms
180            ("variable", Form::with(c.text)),
181            ("variable.builtin", Form::with(c.peach)),
182            ("variable.member", Form::with(c.lavender)),
183            ("constant", Form::with(c.peach)),
184            ("constant.builtin", Form::with(c.peach)),
185            ("module", Form::with(c.blue).italic()),
186            ("label", Form::with(c.green)),
187            ("string", Form::with(c.green)),
188            ("string.escape", Form::with(c.peach)),
189            ("string.special.path", Form::with(c.sky).underlined()),
190            ("character", Form::with(c.peach)),
191            ("boolean", Form::with(c.peach)),
192            ("number", Form::with(c.peach)),
193            ("type", Form::with(c.yellow).italic()),
194            ("type.builtin", Form::with(c.yellow).reset()),
195            ("attribute", Form::with(c.green)),
196            ("property", Form::with(c.text)),
197            ("function", Form::with(c.blue).reset()),
198            ("function.macro", Form::with(c.lavender).italic()),
199            ("constructor", Form::with(c.peach)),
200            ("operator", Form::with(c.sapphire)),
201            ("keyword", Form::with(c.mauve)),
202            ("punctuation.bracket", Form::with(c.subtext0)),
203            ("punctuation.delimiter", Form::with(c.subtext0)),
204            ("comment", Form::with(c.overlay1)),
205            ("comment.documentation", Form::with(c.overlay1).bold()),
206            ("markup", Form::new()),
207            ("markup.strong", Form::with(c.maroon).bold()),
208            ("markup.italic", Form::with(c.maroon).italic()),
209            ("markup.strikethrough", Form::new().crossed_out()),
210            ("markup.underline", Form::underlined()),
211            ("markup.heading", Form::with(c.blue).bold()),
212            ("markup.math", Form::with(c.yellow)),
213            ("markup.quote", Form::with(c.maroon).bold()),
214            ("markup.environment", Form::with(c.pink)),
215            ("markup.environment.name", Form::with(c.blue)),
216            ("markup.link", Form::with(c.lavender).underlined()),
217            ("markup.raw", Form::with(c.teal)),
218            ("markup.list", Form::with(c.yellow)),
219            ("markup.list.checked", Form::with(c.green)),
220            ("markup.list.unchecked", Form::with(c.overlay1)),
221            ("diff.plus", Form::with(c.green)),
222            ("diff.delta", Form::with(c.blue)),
223            ("diff.delta.renamed", Form::with(c.yellow)),
224            ("diff.minus", Form::with(c.red)),
225        );
226
227        (self.modifications)(c)
228    }
229
230    fn name(&self) -> &'static str {
231        match self.flavour {
232            Flavour::Latte => "catppuccin-latte",
233            Flavour::Frappe => "catppuccin-frappe",
234            Flavour::Macchiato => "catppuccin-macchiato",
235            Flavour::Mocha => "catppuccin-mocha",
236        }
237    }
238}
239
240impl ColorScheme {
241    /// Returns the Catppuccin [`ColorScheme`] in the Latte flavour
242    fn latte(modifications: &'static (dyn Fn(Colors) + Send + Sync)) -> Self {
243        Self {
244            flavour: Flavour::Latte,
245            no_background: false,
246            modifications,
247        }
248    }
249
250    /// Returns the Catppuccin [`ColorScheme`] in the Frappe flavour
251    fn frappe(modifications: &'static (dyn Fn(Colors) + Send + Sync)) -> Self {
252        Self {
253            flavour: Flavour::Frappe,
254            no_background: false,
255            modifications,
256        }
257    }
258
259    /// Returns the Catppuccin [`ColorScheme`] in the Macchiato
260    /// flavour
261    fn macchiato(modifications: &'static (dyn Fn(Colors) + Send + Sync)) -> Self {
262        Self {
263            flavour: Flavour::Macchiato,
264            no_background: false,
265            modifications,
266        }
267    }
268
269    /// Returns the Catppuccin [`ColorScheme`] in the Mocha flavour
270    fn mocha(modifications: &'static (dyn Fn(Colors) + Send + Sync)) -> Self {
271        Self {
272            flavour: Flavour::Mocha,
273            no_background: false,
274            modifications,
275        }
276    }
277
278    /// Removes the background color
279    ///
280    /// This can allow, for example, transparent backgrounds in
281    /// terminal interfaces.
282    fn no_bg(self, bool: bool) -> Self {
283        Self { no_background: bool, ..self }
284    }
285}
286
287pub struct Colors {
288    pub rosewater: &'static str,
289    pub flamingo: &'static str,
290    pub pink: &'static str,
291    pub mauve: &'static str,
292    pub red: &'static str,
293    pub maroon: &'static str,
294    pub peach: &'static str,
295    pub yellow: &'static str,
296    pub green: &'static str,
297    pub teal: &'static str,
298    pub sky: &'static str,
299    pub sapphire: &'static str,
300    pub blue: &'static str,
301    pub lavender: &'static str,
302    pub text: &'static str,
303    pub subtext1: &'static str,
304    pub subtext0: &'static str,
305    pub overlay2: &'static str,
306    pub overlay1: &'static str,
307    pub overlay0: &'static str,
308    pub surface2: &'static str,
309    pub surface1: &'static str,
310    pub surface0: &'static str,
311    pub base: &'static str,
312    pub mantle: &'static str,
313    pub crust: &'static str,
314}
315
316const LATTE: Colors = Colors {
317    rosewater: "#dc8a78",
318    flamingo: "#dd7878",
319    pink: "#ea76cb",
320    mauve: "#8839ef",
321    red: "#d20f39",
322    maroon: "#e64553",
323    peach: "#fe640b",
324    yellow: "#df8e1d",
325    green: "#40a02b",
326    teal: "#179299",
327    sky: "#04a5e5",
328    sapphire: "#209fb5",
329    blue: "#1e66f5",
330    lavender: "#7287fd",
331    text: "#4c4f69",
332    subtext1: "#5c5f77",
333    subtext0: "#6c6f85",
334    overlay2: "#7c7f93",
335    overlay1: "#8c8fa1",
336    overlay0: "#9ca0b0",
337    surface2: "#acb0be",
338    surface1: "#bcc0cc",
339    surface0: "#ccd0da",
340    base: "#eff1f5",
341    mantle: "#e6e9ef",
342    crust: "#dce0e8",
343};
344const FRAPPE: Colors = Colors {
345    rosewater: "#f2d5cf",
346    flamingo: "#eebebe",
347    pink: "#f4b8e4",
348    mauve: "#ca9ee6",
349    red: "#e78284",
350    maroon: "#ea999c",
351    peach: "#ef9f76",
352    yellow: "#e5c890",
353    green: "#a6d189",
354    teal: "#81c8be",
355    sky: "#99d1db",
356    sapphire: "#85c1dc",
357    blue: "#8caaee",
358    lavender: "#babbf1",
359    text: "#c6d0f5",
360    subtext1: "#b5bfe2",
361    subtext0: "#a5adce",
362    overlay2: "#949cbb",
363    overlay1: "#838ba7",
364    overlay0: "#737994",
365    surface2: "#626880",
366    surface1: "#51576d",
367    surface0: "#414559",
368    base: "#303446",
369    mantle: "#292c3c",
370    crust: "#232634",
371};
372
373const MACCHIATO: Colors = Colors {
374    rosewater: "#f4dbd6",
375    flamingo: "#f0c6c6",
376    pink: "#f5bde6",
377    mauve: "#c6a0f6",
378    red: "#ed8796",
379    maroon: "#ee99a0",
380    peach: "#f5a97f",
381    yellow: "#eed49f",
382    green: "#a6da95",
383    teal: "#8bd5ca",
384    sky: "#91d7e3",
385    sapphire: "#7dc4e4",
386    blue: "#8aadf4",
387    lavender: "#b7bdf8",
388    text: "#cad3f5",
389    subtext1: "#b8c0e0",
390    subtext0: "#a5adcb",
391    overlay2: "#939ab7",
392    overlay1: "#8087a2",
393    overlay0: "#6e738d",
394    surface2: "#5b6078",
395    surface1: "#494d64",
396    surface0: "#363a4f",
397    base: "#24273a",
398    mantle: "#1e2030",
399    crust: "#181926",
400};
401
402const MOCHA: Colors = Colors {
403    rosewater: "#f5e0dc",
404    flamingo: "#f2cdcd",
405    pink: "#f5c2e7",
406    mauve: "#cba6f7",
407    red: "#f38ba8",
408    maroon: "#eba0ac",
409    peach: "#fab387",
410    yellow: "#f9e2af",
411    green: "#a6e3a1",
412    teal: "#94e2d5",
413    sky: "#89dceb",
414    sapphire: "#74c7ec",
415    blue: "#89b4fa",
416    lavender: "#b4befe",
417    text: "#cdd6f4",
418    subtext1: "#bac2de",
419    subtext0: "#a6adc8",
420    overlay2: "#9399b2",
421    overlay1: "#7f849c",
422    overlay0: "#6c7086",
423    surface2: "#585b70",
424    surface1: "#45475a",
425    surface0: "#313244",
426    base: "#1e1e2e",
427    mantle: "#181825",
428    crust: "#11111b",
429};