Skip to main content

sdml_generate/color/
mod.rs

1/*!
2This module provides syntax coloring support for SDML and RDF.
3*/
4
5use nu_ansi_term::{Color, Style};
6use sdml_errors::diagnostics::UseColor;
7use std::fmt::Display;
8use std::sync::OnceLock;
9use std::sync::RwLock;
10
11// ------------------------------------------------------------------------------------------------
12// Private Macros
13// ------------------------------------------------------------------------------------------------
14
15macro_rules! method {
16    ($name:ident, $el:ident) => {
17        #[inline(always)]
18        fn $name<S>(&self, value: S) -> String
19        where
20            S: AsRef<str>,
21        {
22            self.colorize(LanguageElement::$el, value)
23        }
24    };
25}
26
27// ------------------------------------------------------------------------------------------------
28// Public Types
29// ------------------------------------------------------------------------------------------------
30
31///
32/// These are the language elements used in RDF and SDML and map to the dotted-name format
33/// used in tree-sitter queries.
34///
35#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
36pub enum LanguageElement {
37    Comment,
38    Constant,
39    ConstantDefinition,
40    Embedded,
41    Error,
42    Function,
43    FunctionCall,
44    FunctionDefinition,
45    Keyword,
46    LiteralBoolean,
47    LiteralNumber,
48    LiteralString,
49    LiteralStringSpecial,
50    Operator,
51    Method,
52    MethodDefinition,
53    Module,
54    ModuleDefinition,
55    Property,
56    PropertyDefinition,
57    Punctuation,
58    PunctuationBracket,
59    PunctuationDelimiter,
60    PunctuationSeparator,
61    Type,
62    TypeBuiltin,
63    TypeDefinition,
64    Variable,
65    VariableBuiltin,
66    VariableField,
67    VariableParameter,
68}
69
70///
71/// This trait is implemented by format-specific providers.
72///
73pub trait Colorizer {
74    ///
75    /// Colorize the `value` according to the language element type `el`.
76    ///
77    fn colorize<S>(&self, el: LanguageElement, value: S) -> String
78    where
79        S: AsRef<str>;
80
81    method!(comment, Comment);
82    method!(constant, Constant);
83    method!(constant_definition, ConstantDefinition);
84    method!(embedded, Embedded);
85    method!(error, Error);
86    method!(function, Function);
87    method!(function_call, FunctionCall);
88    method!(function_definition, FunctionDefinition);
89    method!(keyword, Keyword);
90    method!(boolean, LiteralBoolean);
91    method!(number, LiteralNumber);
92    method!(string, LiteralString);
93    method!(string_special, LiteralStringSpecial);
94    method!(url, LiteralStringSpecial);
95    method!(operator, Operator);
96    method!(method, Method);
97    method!(method_definition, MethodDefinition);
98    method!(module, Module);
99    method!(module_definition, ModuleDefinition);
100    method!(property, Property);
101    method!(property_definitions, PropertyDefinition);
102    method!(punctuation, Punctuation);
103    method!(bracket, PunctuationBracket);
104    method!(delimiter, PunctuationDelimiter);
105    method!(separator, PunctuationSeparator);
106    method!(type_ref, Type);
107    method!(type_builtin, TypeBuiltin);
108    method!(type_definition, TypeDefinition);
109    method!(variable, Variable);
110    method!(variable_field, VariableField);
111    method!(variable_parameter, VariableParameter);
112}
113
114///
115/// An implementation of [`Colorizer`] for ANSI terminal coloring.
116///
117#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
118pub struct ConsoleColor {}
119
120///
121/// An implementation of [`Colorizer`] for HTML `pre>code` spans.
122///
123#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
124pub struct HtmlColor {}
125
126// ------------------------------------------------------------------------------------------------
127// Public Functions
128// ------------------------------------------------------------------------------------------------
129
130static COLORIZE: OnceLock<RwLock<UseColor>> = OnceLock::new();
131
132///
133/// Return the value of the global color flag.
134///
135pub fn colorize() -> UseColor {
136    *COLORIZE.get_or_init(init_colorize).read().unwrap()
137}
138
139///
140/// Set the value of the global color flag.
141///
142pub fn set_colorize(colorize: UseColor) {
143    *COLORIZE.get_or_init(init_colorize).write().unwrap() = colorize;
144}
145
146// ------------------------------------------------------------------------------------------------
147// Private Macros
148// ------------------------------------------------------------------------------------------------
149
150// ------------------------------------------------------------------------------------------------
151// Private Types
152// ------------------------------------------------------------------------------------------------
153
154fn init_colorize() -> RwLock<UseColor> {
155    RwLock::new(UseColor::from_env())
156}
157
158// ------------------------------------------------------------------------------------------------
159// Implementations
160// ------------------------------------------------------------------------------------------------
161
162impl Display for LanguageElement {
163    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
164        write!(
165            f,
166            "{}",
167            match self {
168                LanguageElement::Comment => "comment",
169                LanguageElement::Constant => "constant",
170                LanguageElement::ConstantDefinition => "constant.definition",
171                LanguageElement::Embedded => "embedded",
172                LanguageElement::Error => "error",
173                LanguageElement::Function => "function",
174                LanguageElement::FunctionCall => "function.call",
175                LanguageElement::FunctionDefinition => "function.definition",
176                LanguageElement::Keyword => "keyword",
177                LanguageElement::LiteralBoolean => "boolean",
178                LanguageElement::LiteralNumber => "number",
179                LanguageElement::LiteralString => "string",
180                LanguageElement::LiteralStringSpecial => "string.special",
181                LanguageElement::Operator => "operator",
182                LanguageElement::Method => "method",
183                LanguageElement::MethodDefinition => "method.definition",
184                LanguageElement::Module => "module",
185                LanguageElement::ModuleDefinition => "module.definition",
186                LanguageElement::Property => "property",
187                LanguageElement::PropertyDefinition => "property.definition",
188                LanguageElement::Punctuation => "punctuation",
189                LanguageElement::PunctuationBracket => "punctuation.bracket",
190                LanguageElement::PunctuationDelimiter => "punctuation.delimiter",
191                LanguageElement::PunctuationSeparator => "punctuation.separator",
192                LanguageElement::Type => "type",
193                LanguageElement::TypeBuiltin => "type.builtin",
194                LanguageElement::TypeDefinition => "type.definition",
195                LanguageElement::Variable => "variable",
196                LanguageElement::VariableBuiltin => "variable.builtin",
197                LanguageElement::VariableField => "variable.field",
198                LanguageElement::VariableParameter => "variable.parameter",
199            }
200        )
201    }
202}
203
204// ------------------------------------------------------------------------------------------------
205
206impl Colorizer for ConsoleColor {
207    fn colorize<S>(&self, el: LanguageElement, value: S) -> String
208    where
209        S: AsRef<str>,
210    {
211        if colorize().use_color() {
212            self.style(el).paint(value.as_ref()).to_string()
213        } else {
214            value.as_ref().to_string()
215        }
216    }
217}
218
219impl ConsoleColor {
220    pub const fn new() -> Self {
221        Self {}
222    }
223
224    fn style(&self, el: LanguageElement) -> Style {
225        match el {
226            LanguageElement::Comment => Style::new().fg(Color::Fixed(248)).italic(),
227            // Constants
228            LanguageElement::Constant => Style::new().fg(Color::Fixed(58)),
229            LanguageElement::ConstantDefinition => Style::new().fg(Color::Fixed(58)).bold(),
230            // Other languages embedded in this one
231            LanguageElement::Embedded => Style::new().fg(Color::Fixed(248)).italic(),
232            // Those pesky errors
233            LanguageElement::Error => Style::new().fg(Color::Fixed(9)).underline(),
234            // Functions (vs methods)
235            LanguageElement::Function => Style::new().fg(Color::Fixed(26)),
236            LanguageElement::FunctionCall => Style::new().fg(Color::Fixed(26)),
237            LanguageElement::FunctionDefinition => Style::new().fg(Color::Fixed(26)).bold(),
238            // Language Keywords
239            LanguageElement::Keyword => Style::new().fg(Color::Fixed(100)),
240            // Literal values
241            LanguageElement::LiteralBoolean => Style::new().fg(Color::Fixed(58)),
242            LanguageElement::LiteralNumber => Style::new().fg(Color::Fixed(58)),
243            LanguageElement::LiteralString => Style::new().fg(Color::Fixed(70)),
244            LanguageElement::LiteralStringSpecial => Style::new().fg(Color::Fixed(92)),
245            // Operators
246            LanguageElement::Operator => Style::new().fg(Color::Fixed(239)).bold(),
247            // Methods (vs functions)
248            LanguageElement::Method => Style::new().fg(Color::Fixed(26)),
249            LanguageElement::MethodDefinition => Style::new().fg(Color::Fixed(26)).bold(),
250            // Modules
251            LanguageElement::Module => Style::new().fg(Color::Fixed(19)),
252            LanguageElement::ModuleDefinition => Style::new().fg(Color::Fixed(19)).bold(),
253            // Properties/Attributes
254            LanguageElement::Property => Style::new().fg(Color::Fixed(160)),
255            LanguageElement::PropertyDefinition => Style::new().fg(Color::Fixed(160)).bold(),
256            // Punctuation
257            LanguageElement::Punctuation => Style::new().fg(Color::Fixed(239)),
258            LanguageElement::PunctuationBracket => Style::new().fg(Color::Fixed(239)),
259            LanguageElement::PunctuationDelimiter => Style::new().fg(Color::Fixed(239)),
260            LanguageElement::PunctuationSeparator => Style::new().fg(Color::Fixed(239)),
261            // Types
262            LanguageElement::Type => Style::new().fg(Color::Fixed(27)),
263            LanguageElement::TypeBuiltin => Style::new().fg(Color::Fixed(21)),
264            LanguageElement::TypeDefinition => Style::new().fg(Color::Fixed(27)).bold(),
265            // Variables
266            LanguageElement::Variable => Style::new().fg(Color::Fixed(67)),
267            LanguageElement::VariableBuiltin => Style::new().fg(Color::Fixed(67)),
268            LanguageElement::VariableField => Style::new().fg(Color::Fixed(67)),
269            LanguageElement::VariableParameter => Style::new().fg(Color::Fixed(67)),
270        }
271    }
272}
273
274// ------------------------------------------------------------------------------------------------
275
276impl Colorizer for HtmlColor {
277    fn colorize<S>(&self, el: LanguageElement, value: S) -> String
278    where
279        S: AsRef<str>,
280    {
281        if colorize().use_color() {
282            format!(
283                "<span class=\"{}\">{}</span>",
284                el.to_string().replace('.', "-"),
285                value.as_ref()
286            )
287        } else {
288            value.as_ref().to_string()
289        }
290    }
291}
292
293// ------------------------------------------------------------------------------------------------
294// Modules
295// ------------------------------------------------------------------------------------------------
296
297pub mod rdf;
298
299pub mod sdml;