]> git.lizzy.rs Git - rust.git/blob - editors/code/src/scopes.ts
73fabbf549adb2fa154a27d86d7eafaff70421f9
[rust.git] / editors / code / src / scopes.ts
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 // Load all textmate scopes in the currently active theme
13 export function loadThemeColors(): Map<string, TextMateRuleSettings> {
14     // Find out current color theme
15     const themeName = vscode.workspace
16         .getConfiguration('workbench')
17         .get('colorTheme');
18
19     if (typeof themeName !== 'string') {
20         // console.warn('workbench.colorTheme is', themeName)
21         return new Map();
22     }
23     return loadThemeNamed(themeName);
24 }
25
26 function loadThemeNamed(themeName: string): Map<string, TextMateRuleSettings> {
27     function isTheme(extension: vscode.Extension<any>): boolean {
28         return (
29             extension.extensionKind === vscode.ExtensionKind.UI &&
30             extension.packageJSON.contributes &&
31             extension.packageJSON.contributes.themes
32         );
33     }
34
35     let themePaths = vscode.extensions.all
36         .filter(isTheme)
37         .flatMap(ext => {
38             return ext.packageJSON.contributes.themes
39                 .filter((it: any) => (it.id || it.label) === themeName)
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     }
47
48     const customizations: any = vscode.workspace.getConfiguration('editor').get('tokenColorCustomizations');
49     mergeInto(res, loadColors(customizations?.textMateRules ?? []))
50
51     return res;
52 }
53
54 function loadThemeFile(themePath: string): Map<string, TextMateRuleSettings> {
55     let text;
56     try {
57         text = fs.readFileSync(themePath, 'utf8')
58     } catch {
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);
69     }
70
71     return res;
72 }
73
74 interface TextMateRule {
75     scope: string | string[];
76     settings: TextMateRuleSettings;
77 }
78
79 function loadColors(textMateRules: TextMateRule[]): Map<string, TextMateRuleSettings> {
80     const res = new Map();
81     for (const rule of textMateRules) {
82         const scopes = typeof rule.scope === 'string'
83             ? [rule.scope]
84             : rule.scope;
85         for (const scope of scopes) {
86             res.set(scope, rule.settings)
87         }
88     }
89     return res
90 }
91
92 function mergeRuleSettings(
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     }
101 }
102
103 function mergeInto(dst: Map<string, TextMateRuleSettings>, addition: Map<string, TextMateRuleSettings>) {
104     addition.forEach((value, key) => {
105         const merged = mergeRuleSettings(dst.get(key), value)
106         dst.set(key, merged)
107     })
108 }