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';
6 export interface TextMateRule {
7 scope: string | string[];
8 settings: TextMateRuleSettings;
11 export interface TextMateRuleSettings {
17 // Current theme colors
18 const rules = new Map<string, TextMateRuleSettings>();
20 export function find(scope: string): TextMateRuleSettings | undefined {
21 return rules.get(scope);
24 // Load all textmate scopes in the currently active theme
25 export function load() {
26 // Remove any previous theme
28 // Find out current color theme
29 const themeName = vscode.workspace
30 .getConfiguration('workbench')
33 if (typeof themeName !== 'string') {
34 // console.warn('workbench.colorTheme is', themeName)
37 // Try to load colors from that theme
39 loadThemeNamed(themeName);
41 // console.warn('failed to load theme', themeName, e)
47 // Find current theme on disk
48 function loadThemeNamed(themeName: string) {
49 function isTheme(extension: vscode.Extension<any>): boolean {
51 extension.extensionKind === vscode.ExtensionKind.UI &&
52 extension.packageJSON.contributes &&
53 extension.packageJSON.contributes.themes
57 const themePaths = vscode.extensions.all
59 .reduce((list, extension) => {
60 return extension.packageJSON.contributes.themes
63 (element.id || element.label) === themeName,
65 .map((element: any) =>
66 path.join(extension.extensionPath, element.path),
71 themePaths.forEach(loadThemeFile);
73 const tokenColorCustomizations: [any] = [
75 .getConfiguration('editor')
76 .get('tokenColorCustomizations'),
79 tokenColorCustomizations
80 .filter(custom => custom && custom.textMateRules)
81 .map(custom => custom.textMateRules)
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);
93 .filter(theme => theme.tokenColors)
94 .map(theme => theme.tokenColors)
98 .filter(theme => theme.include)
99 .map(theme => path.join(path.dirname(themePath), theme.include))
100 .forEach(loadThemeFile);
103 function mergeRuleSettings(
104 defaultSetting: TextMateRuleSettings | undefined,
105 override: TextMateRuleSettings,
106 ): TextMateRuleSettings {
107 if (defaultSetting === undefined) {
110 const mergedRule = defaultSetting;
112 mergedRule.background = override.background || defaultSetting.background;
113 mergedRule.foreground = override.foreground || defaultSetting.foreground;
114 mergedRule.fontStyle = override.fontStyle || defaultSetting.foreground;
119 function updateRules(
121 updatedSettings: TextMateRuleSettings,
124 .map(settings => mergeRuleSettings(settings, updatedSettings))
125 .forEach(settings => rules.set(scope, settings));
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));