@@ -3,49 +3,27 @@ import * as jsonc from 'jsonc-parser';
3
3
import * as path from 'path' ;
4
4
import * as vscode from 'vscode' ;
5
5
6
- export interface TextMateRule {
7
- scope : string | string [ ] ;
8
- settings : TextMateRuleSettings ;
9
- }
10
-
11
6
export interface TextMateRuleSettings {
12
7
foreground ?: string ;
13
8
background ?: string ;
14
9
fontStyle ?: string ;
15
10
}
16
11
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
12
// Load all textmate scopes in the currently active theme
25
- export function load ( ) {
26
- // Remove any previous theme
27
- rules . clear ( ) ;
13
+ export function loadThemeColors ( ) : Map < string , TextMateRuleSettings > {
28
14
// Find out current color theme
29
15
const themeName = vscode . workspace
30
16
. getConfiguration ( 'workbench' )
31
17
. get ( 'colorTheme' ) ;
32
18
33
19
if ( typeof themeName !== 'string' ) {
34
20
// 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)
21
+ return new Map ( ) ;
42
22
}
23
+ return loadThemeNamed ( themeName ) ;
43
24
}
44
25
45
-
46
-
47
- // Find current theme on disk
48
- function loadThemeNamed ( themeName : string ) {
26
+ function loadThemeNamed ( themeName : string ) : Map < string , TextMateRuleSettings > {
49
27
function isTheme ( extension : vscode . Extension < any > ) : boolean {
50
28
return (
51
29
extension . extensionKind === vscode . ExtensionKind . UI &&
@@ -54,83 +32,77 @@ function loadThemeNamed(themeName: string) {
54
32
) ;
55
33
}
56
34
57
- const themePaths = vscode . extensions . all
35
+ let themePaths = vscode . extensions . all
58
36
. 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 ) ;
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
+ }
72
47
73
- const tokenColorCustomizations : [ any ] = [
74
- vscode . workspace
75
- . getConfiguration ( 'editor' )
76
- . get ( 'tokenColorCustomizations' ) ,
77
- ] ;
48
+ const customizations : any = vscode . workspace . getConfiguration ( 'editor' ) . get ( 'tokenColorCustomizations' ) ;
49
+ mergeInto ( res , loadColors ( customizations ?. textMateRules ?? [ ] ) )
78
50
79
- tokenColorCustomizations
80
- . filter ( custom => custom && custom . textMateRules )
81
- . map ( custom => custom . textMateRules )
82
- . forEach ( loadColors ) ;
51
+ return res ;
83
52
}
84
53
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 ) ;
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
+ }
91
73
92
- themeContent
93
- . filter ( theme => theme . tokenColors )
94
- . map ( theme => theme . tokenColors )
95
- . forEach ( loadColors ) ;
74
+ interface TextMateRule {
75
+ scope : string | string [ ] ;
76
+ settings : TextMateRuleSettings ;
77
+ }
96
78
97
- themeContent
98
- . filter ( theme => theme . include )
99
- . map ( theme => path . join ( path . dirname ( themePath ) , theme . include ) )
100
- . forEach ( loadThemeFile ) ;
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
101
90
}
102
91
103
92
function mergeRuleSettings (
104
93
defaultSetting : TextMateRuleSettings | undefined ,
105
94
override : TextMateRuleSettings ,
106
95
) : TextMateRuleSettings {
107
- if ( defaultSetting === undefined ) {
108
- return override ;
96
+ return {
97
+ foreground : defaultSetting ?. foreground ?? override . foreground ,
98
+ background : defaultSetting ?. background ?? override . background ,
99
+ fontStyle : defaultSetting ?. fontStyle ?? override . fontStyle ,
109
100
}
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
101
}
118
102
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
- } ) ;
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
+ } )
136
108
}
0 commit comments