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}