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.ts134
1 files changed, 134 insertions, 0 deletions
diff --git a/editors/code/src/scopes.ts b/editors/code/src/scopes.ts
new file mode 100644
index 000000000..5d4395930
--- /dev/null
+++ b/editors/code/src/scopes.ts
@@ -0,0 +1,134 @@
1import * as fs from 'fs'
2import * as path from 'path'
3import * as vscode from 'vscode'
4
5
6
7export interface TextMateRule {
8 scope: string | string[]
9 settings: TextMateRuleSettings
10}
11
12export interface TextMateRuleSettings {
13 foreground: string | undefined
14 background: string | undefined
15 fontStyle: string | undefined
16}
17
18// Current theme colors
19const rules = new Map<string, TextMateRuleSettings>()
20
21export function find(scope: string): TextMateRuleSettings | undefined {
22 return rules.get(scope)
23}
24
25// Load all textmate scopes in the currently active theme
26export function load() {
27 // Remove any previous theme
28 rules.clear()
29 // Find out current color theme
30 const themeName = vscode.workspace.getConfiguration('workbench').get('colorTheme')
31
32 if (typeof themeName !== 'string') {
33 // console.warn('workbench.colorTheme is', themeName)
34 return
35 }
36 // Try to load colors from that theme
37 try {
38 loadThemeNamed(themeName)
39 } catch (e) {
40 // console.warn('failed to load theme', themeName, e)
41 }
42}
43
44function filterThemeExtensions(extension: vscode.Extension<any>): boolean {
45 return extension.extensionKind === vscode.ExtensionKind.UI &&
46 extension.packageJSON.contributes &&
47 extension.packageJSON.contributes.themes
48}
49
50
51
52// Find current theme on disk
53function loadThemeNamed(themeName: string) {
54
55 const themePaths = vscode.extensions.all
56 .filter(filterThemeExtensions)
57 .reduce((list, extension) => {
58 return extension.packageJSON.contributes.themes
59 .filter((element: any) => (element.id || element.label) === themeName)
60 .map((element: any) => path.join(extension.extensionPath, element.path))
61 .concat(list)
62 }, Array<string>())
63
64
65 themePaths.forEach(loadThemeFile)
66
67 const tokenColorCustomizations: [any] = [vscode.workspace.getConfiguration('editor').get('tokenColorCustomizations')]
68
69 tokenColorCustomizations
70 .filter(custom => custom && custom.textMateRules)
71 .map(custom => custom.textMateRules)
72 .forEach(loadColors)
73
74}
75
76
77function loadThemeFile(themePath: string) {
78 const themeContent = [themePath]
79 .filter(isFile)
80 .map(readFileText)
81 .map(parseJSON)
82 .filter(theme => theme)
83
84 themeContent
85 .filter(theme => theme.tokenColors)
86 .map(theme => theme.tokenColors)
87 .forEach(loadColors)
88
89 themeContent
90 .filter(theme => theme.include)
91 .map(theme => path.join(path.dirname(themePath), theme.include))
92 .forEach(loadThemeFile)
93}
94
95function mergeRuleSettings(defaultSetting: TextMateRuleSettings | undefined, override: TextMateRuleSettings): TextMateRuleSettings {
96 if (defaultSetting === undefined) { return override }
97 const mergedRule = defaultSetting
98
99 mergedRule.background = override.background || defaultSetting.background
100 mergedRule.foreground = override.foreground || defaultSetting.foreground
101 mergedRule.fontStyle = override.fontStyle || defaultSetting.foreground
102
103 return mergedRule
104}
105
106function updateRules(scope: string, updatedSettings: TextMateRuleSettings): void {
107 [rules.get(scope)]
108 .map(settings => mergeRuleSettings(settings, updatedSettings))
109 .forEach(settings => rules.set(scope, settings))
110}
111
112function loadColors(textMateRules: TextMateRule[]): void {
113 textMateRules.forEach(rule => {
114 if (typeof rule.scope === 'string') {
115 updateRules(rule.scope, rule.settings)
116 }
117 else if (rule.scope instanceof Array) {
118 rule.scope.forEach(scope => updateRules(scope, rule.settings))
119 }
120 })
121}
122
123function isFile(filePath: string): boolean {
124 return [filePath].map(fs.statSync).every(stat => stat.isFile())
125}
126
127function readFileText(filePath: string): string {
128 return fs.readFileSync(filePath, 'utf8')
129}
130
131// Might need to replace with JSONC if a theme contains comments.
132function parseJSON(content: string): any {
133 return JSON.parse(content)
134} \ No newline at end of file