aboutsummaryrefslogtreecommitdiff
path: root/editors/code/src/scopes.ts
diff options
context:
space:
mode:
Diffstat (limited to 'editors/code/src/scopes.ts')
-rw-r--r--editors/code/src/scopes.ts146
1 files changed, 146 insertions, 0 deletions
diff --git a/editors/code/src/scopes.ts b/editors/code/src/scopes.ts
new file mode 100644
index 000000000..cb250b853
--- /dev/null
+++ b/editors/code/src/scopes.ts
@@ -0,0 +1,146 @@
1import * as fs from 'fs';
2import * as jsonc from 'jsonc-parser';
3import * as path from 'path';
4import * as vscode from 'vscode';
5
6export interface TextMateRule {
7 scope: string | string[];
8 settings: TextMateRuleSettings;
9}
10
11export interface TextMateRuleSettings {
12 foreground: string | undefined;
13 background: string | undefined;
14 fontStyle: string | undefined;
15}
16
17// Current theme colors
18const rules = new Map<string, TextMateRuleSettings>();
19
20export function find(scope: string): TextMateRuleSettings | undefined {
21 return rules.get(scope);
22}
23
24// Load all textmate scopes in the currently active theme
25export function load() {
26 // Remove any previous theme
27 rules.clear();
28 // Find out current color theme
29 const themeName = vscode.workspace
30 .getConfiguration('workbench')
31 .get('colorTheme');
32
33 if (typeof themeName !== 'string') {
34 // console.warn('workbench.colorTheme is', themeName)
35 return;
36 }
37 // Try to load colors from that theme
38 try {
39 loadThemeNamed(themeName);
40 } catch (e) {
41 // console.warn('failed to load theme', themeName, e)
42 }
43}
44
45function filterThemeExtensions(extension: vscode.Extension<any>): boolean {
46 return (
47 extension.extensionKind === vscode.ExtensionKind.UI &&
48 extension.packageJSON.contributes &&
49 extension.packageJSON.contributes.themes
50 );
51}
52
53// Find current theme on disk
54function loadThemeNamed(themeName: string) {
55 const themePaths = vscode.extensions.all
56 .filter(filterThemeExtensions)
57 .reduce((list, extension) => {
58 return extension.packageJSON.contributes.themes
59 .filter(
60 (element: any) =>
61 (element.id || element.label) === themeName,
62 )
63 .map((element: any) =>
64 path.join(extension.extensionPath, element.path),
65 )
66 .concat(list);
67 }, Array<string>());
68
69 themePaths.forEach(loadThemeFile);
70
71 const tokenColorCustomizations: [any] = [
72 vscode.workspace
73 .getConfiguration('editor')
74 .get('tokenColorCustomizations'),
75 ];
76
77 tokenColorCustomizations
78 .filter(custom => custom && custom.textMateRules)
79 .map(custom => custom.textMateRules)
80 .forEach(loadColors);
81}
82
83function loadThemeFile(themePath: string) {
84 const themeContent = [themePath]
85 .filter(isFile)
86 .map(readFileText)
87 .map(parseJSON)
88 .filter(theme => theme);
89
90 themeContent
91 .filter(theme => theme.tokenColors)
92 .map(theme => theme.tokenColors)
93 .forEach(loadColors);
94
95 themeContent
96 .filter(theme => theme.include)
97 .map(theme => path.join(path.dirname(themePath), theme.include))
98 .forEach(loadThemeFile);
99}
100
101function mergeRuleSettings(
102 defaultSetting: TextMateRuleSettings | undefined,
103 override: TextMateRuleSettings,
104): TextMateRuleSettings {
105 if (defaultSetting === undefined) {
106 return override;
107 }
108 const mergedRule = defaultSetting;
109
110 mergedRule.background = override.background || defaultSetting.background;
111 mergedRule.foreground = override.foreground || defaultSetting.foreground;
112 mergedRule.fontStyle = override.fontStyle || defaultSetting.foreground;
113
114 return mergedRule;
115}
116
117function updateRules(
118 scope: string,
119 updatedSettings: TextMateRuleSettings,
120): void {
121 [rules.get(scope)]
122 .map(settings => mergeRuleSettings(settings, updatedSettings))
123 .forEach(settings => rules.set(scope, settings));
124}
125
126function loadColors(textMateRules: TextMateRule[]): void {
127 textMateRules.forEach(rule => {
128 if (typeof rule.scope === 'string') {
129 updateRules(rule.scope, rule.settings);
130 } else if (rule.scope instanceof Array) {
131 rule.scope.forEach(scope => updateRules(scope, rule.settings));
132 }
133 });
134}
135
136function isFile(filePath: string): boolean {
137 return [filePath].map(fs.statSync).every(stat => stat.isFile());
138}
139
140function readFileText(filePath: string): string {
141 return fs.readFileSync(filePath, 'utf8');
142}
143
144function parseJSON(content: string): any {
145 return jsonc.parse(content);
146}