syntax.ts

 1import deepmerge from "deepmerge"
 2import { font_weights, ThemeConfigInputSyntax, RampSet } from "../common"
 3import { Syntax, SyntaxHighlightStyle, allSyntaxKeys } from "../types/syntax"
 4
 5// Apply defaults to any missing syntax properties that are not defined manually
 6function apply_defaults(
 7    ramps: RampSet,
 8    syntax_highlights: Partial<Syntax>
 9): Syntax {
10    const restKeys: (keyof Syntax)[] = allSyntaxKeys.filter(
11        (key) => !syntax_highlights[key]
12    )
13
14    const completeSyntax: Syntax = {} as Syntax
15
16    const defaults: SyntaxHighlightStyle = {
17        color: ramps.neutral(1).hex(),
18    }
19
20    for (const key of restKeys) {
21        {
22            completeSyntax[key] = {
23                ...defaults,
24            }
25        }
26    }
27
28    const mergedBaseSyntax = Object.assign(completeSyntax, syntax_highlights)
29
30    return mergedBaseSyntax
31}
32
33// Merge the base syntax with the theme syntax overrides
34// This is a deep merge, so any nested properties will be merged as well
35// This allows for a theme to only override a single property of a syntax highlight style
36const merge_syntax = (
37    baseSyntax: Syntax,
38    theme_syntax_overrides: ThemeConfigInputSyntax
39): Syntax => {
40    return deepmerge<Syntax, ThemeConfigInputSyntax>(
41        baseSyntax,
42        theme_syntax_overrides,
43        {
44            arrayMerge: (destinationArray, sourceArray) => [
45                ...destinationArray,
46                ...sourceArray,
47            ],
48        }
49    )
50}
51
52/** Returns a complete Syntax object of the combined styles of a theme's syntax overrides and the default syntax styles */
53export const syntaxStyle = (
54    ramps: RampSet,
55    theme_syntax_overrides: ThemeConfigInputSyntax
56): Syntax => {
57    const syntax_highlights: Partial<Syntax> = {
58        comment: { color: ramps.neutral(0.71).hex() },
59        "comment.doc": { color: ramps.neutral(0.71).hex() },
60        primary: { color: ramps.neutral(1).hex() },
61        emphasis: { color: ramps.blue(0.5).hex() },
62        "emphasis.strong": {
63            color: ramps.blue(0.5).hex(),
64            weight: font_weights.bold,
65        },
66        link_uri: { color: ramps.green(0.5).hex(), underline: true },
67        link_text: { color: ramps.orange(0.5).hex(), italic: true },
68        "text.literal": { color: ramps.orange(0.5).hex() },
69        punctuation: { color: ramps.neutral(0.86).hex() },
70        "punctuation.bracket": { color: ramps.neutral(0.86).hex() },
71        "punctuation.special": { color: ramps.neutral(0.86).hex() },
72        "punctuation.delimiter": { color: ramps.neutral(0.86).hex() },
73        "punctuation.list_marker": { color: ramps.neutral(0.86).hex() },
74        string: { color: ramps.orange(0.5).hex() },
75        "string.special": { color: ramps.orange(0.5).hex() },
76        "string.special.symbol": { color: ramps.orange(0.5).hex() },
77        "string.escape": { color: ramps.neutral(0.71).hex() },
78        "string.regex": { color: ramps.orange(0.5).hex() },
79        "method.constructor": { color: ramps.blue(0.5).hex() },
80        type: { color: ramps.cyan(0.5).hex() },
81        label: { color: ramps.blue(0.5).hex() },
82        attribute: { color: ramps.blue(0.5).hex() },
83        property: { color: ramps.blue(0.5).hex() },
84        constant: { color: ramps.green(0.5).hex() },
85        keyword: { color: ramps.blue(0.5).hex() },
86        operator: { color: ramps.orange(0.5).hex() },
87        number: { color: ramps.green(0.5).hex() },
88        boolean: { color: ramps.green(0.5).hex() },
89        function: { color: ramps.yellow(0.5).hex() },
90    }
91
92    const baseSyntax = apply_defaults(ramps, syntax_highlights)
93    const mergedSyntax = merge_syntax(baseSyntax, theme_syntax_overrides)
94    return mergedSyntax
95}