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