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