diff options
Diffstat (limited to 'editors/code/src/color_theme.ts')
-rw-r--r-- | editors/code/src/color_theme.ts | 123 |
1 files changed, 123 insertions, 0 deletions
diff --git a/editors/code/src/color_theme.ts b/editors/code/src/color_theme.ts new file mode 100644 index 000000000..cbad47f35 --- /dev/null +++ b/editors/code/src/color_theme.ts | |||
@@ -0,0 +1,123 @@ | |||
1 | import * as fs from 'fs'; | ||
2 | import * as jsonc from 'jsonc-parser'; | ||
3 | import * as path from 'path'; | ||
4 | import * as vscode from 'vscode'; | ||
5 | |||
6 | export interface TextMateRuleSettings { | ||
7 | foreground?: string; | ||
8 | background?: string; | ||
9 | fontStyle?: string; | ||
10 | } | ||
11 | |||
12 | export class ColorTheme { | ||
13 | private rules: Map<string, TextMateRuleSettings> = new Map(); | ||
14 | |||
15 | static load(): ColorTheme { | ||
16 | // Find out current color theme | ||
17 | const themeName = vscode.workspace | ||
18 | .getConfiguration('workbench') | ||
19 | .get('colorTheme'); | ||
20 | |||
21 | if (typeof themeName !== 'string') { | ||
22 | // console.warn('workbench.colorTheme is', themeName) | ||
23 | return new ColorTheme(); | ||
24 | } | ||
25 | return loadThemeNamed(themeName); | ||
26 | } | ||
27 | |||
28 | static fromRules(rules: TextMateRule[]): ColorTheme { | ||
29 | const res = new ColorTheme(); | ||
30 | for (const rule of rules) { | ||
31 | const scopes = typeof rule.scope === 'string' | ||
32 | ? [rule.scope] | ||
33 | : rule.scope; | ||
34 | for (const scope of scopes) { | ||
35 | res.rules.set(scope, rule.settings); | ||
36 | } | ||
37 | } | ||
38 | return res; | ||
39 | } | ||
40 | |||
41 | lookup(scopes: string[]): TextMateRuleSettings { | ||
42 | let res: TextMateRuleSettings = {}; | ||
43 | for (const scope of scopes) { | ||
44 | this.rules.forEach((value, key) => { | ||
45 | if (scope.startsWith(key)) { | ||
46 | res = mergeRuleSettings(res, value); | ||
47 | } | ||
48 | }); | ||
49 | } | ||
50 | return res; | ||
51 | } | ||
52 | |||
53 | mergeFrom(other: ColorTheme) { | ||
54 | other.rules.forEach((value, key) => { | ||
55 | const merged = mergeRuleSettings(this.rules.get(key), value); | ||
56 | this.rules.set(key, merged); | ||
57 | }); | ||
58 | } | ||
59 | } | ||
60 | |||
61 | function loadThemeNamed(themeName: string): ColorTheme { | ||
62 | function isTheme(extension: vscode.Extension<any>): boolean { | ||
63 | return ( | ||
64 | extension.extensionKind === vscode.ExtensionKind.UI && | ||
65 | extension.packageJSON.contributes && | ||
66 | extension.packageJSON.contributes.themes | ||
67 | ); | ||
68 | } | ||
69 | |||
70 | let themePaths = vscode.extensions.all | ||
71 | .filter(isTheme) | ||
72 | .flatMap(ext => { | ||
73 | return ext.packageJSON.contributes.themes | ||
74 | .filter((it: any) => (it.id || it.label) === themeName) | ||
75 | .map((it: any) => path.join(ext.extensionPath, it.path)); | ||
76 | }); | ||
77 | |||
78 | const res = new ColorTheme(); | ||
79 | for (const themePath of themePaths) { | ||
80 | res.mergeFrom(loadThemeFile(themePath)); | ||
81 | } | ||
82 | |||
83 | const customizations: any = vscode.workspace.getConfiguration('editor').get('tokenColorCustomizations'); | ||
84 | res.mergeFrom(ColorTheme.fromRules(customizations?.textMateRules ?? [])); | ||
85 | |||
86 | return res; | ||
87 | } | ||
88 | |||
89 | function loadThemeFile(themePath: string): ColorTheme { | ||
90 | let text; | ||
91 | try { | ||
92 | text = fs.readFileSync(themePath, 'utf8'); | ||
93 | } catch { | ||
94 | return new ColorTheme(); | ||
95 | } | ||
96 | const obj = jsonc.parse(text); | ||
97 | const tokenColors = obj?.tokenColors ?? []; | ||
98 | const res = ColorTheme.fromRules(tokenColors); | ||
99 | |||
100 | for (const include in obj?.include ?? []) { | ||
101 | const includePath = path.join(path.dirname(themePath), include); | ||
102 | const tmp = loadThemeFile(includePath); | ||
103 | res.mergeFrom(tmp); | ||
104 | } | ||
105 | |||
106 | return res; | ||
107 | } | ||
108 | |||
109 | interface TextMateRule { | ||
110 | scope: string | string[]; | ||
111 | settings: TextMateRuleSettings; | ||
112 | } | ||
113 | |||
114 | function mergeRuleSettings( | ||
115 | defaultSetting: TextMateRuleSettings | undefined, | ||
116 | override: TextMateRuleSettings, | ||
117 | ): TextMateRuleSettings { | ||
118 | return { | ||
119 | foreground: override.foreground ?? defaultSetting?.foreground, | ||
120 | background: override.background ?? defaultSetting?.background, | ||
121 | fontStyle: override.fontStyle ?? defaultSetting?.fontStyle, | ||
122 | }; | ||
123 | } | ||