diff options
Diffstat (limited to 'editors/code/src/scopes.ts')
-rw-r--r-- | editors/code/src/scopes.ts | 182 |
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'; | |||
3 | import * as path from 'path'; | 3 | import * as path from 'path'; |
4 | import * as vscode from 'vscode'; | 4 | import * as vscode from 'vscode'; |
5 | 5 | ||
6 | export interface TextMateRule { | ||
7 | scope: string | string[]; | ||
8 | settings: TextMateRuleSettings; | ||
9 | } | ||
10 | |||
11 | export interface TextMateRuleSettings { | 6 | export 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 | ||
18 | const rules = new Map<string, TextMateRuleSettings>(); | ||
19 | |||
20 | export 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 |
25 | export function load() { | 13 | export 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 | ||
45 | function filterThemeExtensions(extension: vscode.Extension<any>): boolean { | 26 | function 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 | ||
54 | function 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 | ||
83 | function 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 | ||
101 | function mergeRuleSettings( | 54 | function 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 | ||
117 | function updateRules( | 74 | interface 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 | ||
126 | function loadColors(textMateRules: TextMateRule[]): void { | 79 | function 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 | |||
136 | function isFile(filePath: string): boolean { | ||
137 | return [filePath].map(fs.statSync).every(stat => stat.isFile()); | ||
138 | } | 90 | } |
139 | 91 | ||
140 | function readFileText(filePath: string): string { | 92 | function 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 | ||
144 | function parseJSON(content: string): any { | 103 | function 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 | } |