]> git.lizzy.rs Git - rust.git/blob - editors/code/src/scopes.ts
Reduce scope
[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 TextMateRule {
7     scope: string | string[];
8     settings: TextMateRuleSettings;
9 }
10
11 export interface TextMateRuleSettings {
12     foreground?: string;
13     background?: string;
14     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 }
23
24 // Load all textmate scopes in the currently active theme
25 export 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
45
46
47 // Find current theme on disk
48 function loadThemeNamed(themeName: string) {
49     function isTheme(extension: vscode.Extension<any>): boolean {
50         return (
51             extension.extensionKind === vscode.ExtensionKind.UI &&
52             extension.packageJSON.contributes &&
53             extension.packageJSON.contributes.themes
54         );
55     }
56
57     const themePaths = vscode.extensions.all
58         .filter(isTheme)
59         .reduce((list, extension) => {
60             return extension.packageJSON.contributes.themes
61                 .filter(
62                     (element: any) =>
63                         (element.id || element.label) === themeName,
64                 )
65                 .map((element: any) =>
66                     path.join(extension.extensionPath, element.path),
67                 )
68                 .concat(list);
69         }, Array<string>());
70
71     themePaths.forEach(loadThemeFile);
72
73     const tokenColorCustomizations: [any] = [
74         vscode.workspace
75             .getConfiguration('editor')
76             .get('tokenColorCustomizations'),
77     ];
78
79     tokenColorCustomizations
80         .filter(custom => custom && custom.textMateRules)
81         .map(custom => custom.textMateRules)
82         .forEach(loadColors);
83 }
84
85 function loadThemeFile(themePath: string) {
86     const themeContent = [themePath]
87         .filter(it => fs.statSync(it).isFile())
88         .map(it => fs.readFileSync(it, 'utf8'))
89         .map(it => jsonc.parse(it))
90         .filter(theme => theme);
91
92     themeContent
93         .filter(theme => theme.tokenColors)
94         .map(theme => theme.tokenColors)
95         .forEach(loadColors);
96
97     themeContent
98         .filter(theme => theme.include)
99         .map(theme => path.join(path.dirname(themePath), theme.include))
100         .forEach(loadThemeFile);
101 }
102
103 function mergeRuleSettings(
104     defaultSetting: TextMateRuleSettings | undefined,
105     override: TextMateRuleSettings,
106 ): TextMateRuleSettings {
107     if (defaultSetting === undefined) {
108         return override;
109     }
110     const mergedRule = defaultSetting;
111
112     mergedRule.background = override.background || defaultSetting.background;
113     mergedRule.foreground = override.foreground || defaultSetting.foreground;
114     mergedRule.fontStyle = override.fontStyle || defaultSetting.foreground;
115
116     return mergedRule;
117 }
118
119 function updateRules(
120     scope: string,
121     updatedSettings: TextMateRuleSettings,
122 ): void {
123     [rules.get(scope)]
124         .map(settings => mergeRuleSettings(settings, updatedSettings))
125         .forEach(settings => rules.set(scope, settings));
126 }
127
128 function loadColors(textMateRules: TextMateRule[]): void {
129     textMateRules.forEach(rule => {
130         if (typeof rule.scope === 'string') {
131             updateRules(rule.scope, rule.settings);
132         } else if (rule.scope instanceof Array) {
133             rule.scope.forEach(scope => updateRules(scope, rule.settings));
134         }
135     });
136 }