1
- import { injectable } from '@theia/core/shared/inversify' ;
2
- import { MonacoThemingService as TheiaMonacoThemingService } from '@theia/monaco/lib/browser/monaco-theming-service' ;
3
- import { ArduinoThemes } from '../core/theming' ;
1
+ import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application' ;
2
+ import { BuiltinThemeProvider } from '@theia/core/lib/browser/theming' ;
3
+ import { DisposableCollection } from '@theia/core/lib/common/disposable' ;
4
+ import URI from '@theia/core/lib/common/uri' ;
5
+ import { inject , injectable } from '@theia/core/shared/inversify' ;
6
+ import {
7
+ deleteTheme as deleteThemeFromIndexedDB ,
8
+ getThemes as getThemesFromIndexedDB ,
9
+ } from '@theia/monaco/lib/browser/monaco-indexed-db' ;
10
+ import {
11
+ MonacoTheme ,
12
+ MonacoThemingService as TheiaMonacoThemingService ,
13
+ } from '@theia/monaco/lib/browser/monaco-theming-service' ;
14
+ import { HostedPluginSupport } from '@theia/plugin-ext/lib/hosted/browser/hosted-plugin' ;
15
+ import { ArduinoThemes , builtInThemeIds } from '../core/theming' ;
4
16
5
17
@injectable ( )
6
18
export class MonacoThemingService extends TheiaMonacoThemingService {
19
+ private readonly _pluginContributedThemeIds = new Set < string > ( ) ;
20
+
7
21
protected override restore ( ) : Promise < void > {
8
22
const { Light, Dark } = ArduinoThemes ;
9
23
this . registerParsedTheme ( {
@@ -19,9 +33,95 @@ export class MonacoThemingService extends TheiaMonacoThemingService {
19
33
json : require ( '../../../../src/browser/data/dark.color-theme.json' ) ,
20
34
} ) ;
21
35
// The custom theme registration must happen before restoring the themes.
22
- // Otherwise, the theme content change is not picked up by Theia.
23
- // There is a indexedDB getAll before the DB put.
36
+ // Otherwise, theme changes are not picked up.
24
37
// https://github.com/arduino/arduino-ide/issues/1251#issuecomment-1436737702
25
38
return super . restore ( ) ;
26
39
}
40
+
41
+ // Customized to populate `_pluginContributedThemeIds`. The rest of the code is vanilla Theia.
42
+ protected override async doRegister (
43
+ theme : MonacoTheme ,
44
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
45
+ pending : { [ uri : string ] : Promise < any > } ,
46
+ toDispose : DisposableCollection
47
+ ) : Promise < void > {
48
+ try {
49
+ const includes = { } ;
50
+ const json = await this . loadTheme (
51
+ theme . uri ,
52
+ includes ,
53
+ pending ,
54
+ toDispose
55
+ ) ;
56
+ if ( toDispose . disposed ) {
57
+ return ;
58
+ }
59
+ const label = theme . label || new URI ( theme . uri ) . path . base ;
60
+ const { id, description, uiTheme } = theme ;
61
+ toDispose . push (
62
+ this . registerParsedTheme ( {
63
+ id,
64
+ label,
65
+ description,
66
+ uiTheme : uiTheme ,
67
+ json,
68
+ includes,
69
+ } )
70
+ ) ;
71
+ // This implementation depends on how Theia internally works.
72
+ // Collect all theme IDs contributed by VSIXs.
73
+ // When all VISXs are loaded, IDE2 checks the indexedDB for registered themes,
74
+ // and if there are obsolete ones, deletes them. A theme is obsolete if it was
75
+ // in the DB but was not loaded from a VSIX during the startup.
76
+ // See https://github.com/eclipse-theia/theia/issues/11151.
77
+ if ( new URI ( theme . uri ) . scheme === 'file' ) {
78
+ this . _pluginContributedThemeIds . add ( theme . id ?? label ) ;
79
+ }
80
+ } catch ( e ) {
81
+ console . error ( 'Failed to load theme from ' + theme . uri , e ) ;
82
+ }
83
+ }
84
+
85
+ get pluginContributedThemeIds ( ) : string [ ] {
86
+ return Array . from ( this . _pluginContributedThemeIds . values ( ) ) ;
87
+ }
88
+ }
89
+
90
+ const compiledThemeIds = new Set ( [
91
+ ...builtInThemeIds . values ( ) ,
92
+ BuiltinThemeProvider . lightTheme ,
93
+ BuiltinThemeProvider . darkTheme ,
94
+ ] ) ;
95
+
96
+ /**
97
+ * Workaround for removing VSIX themes from the indexedDB if they do not load during app startup.
98
+ * This logic cannot be in MonacoThemingService due to a cycle in the DI object graph.
99
+ */
100
+ @injectable ( )
101
+ export class ObsoleteThemesCleanup implements FrontendApplicationContribution {
102
+ @inject ( HostedPluginSupport )
103
+ private readonly hostedPlugin : HostedPluginSupport ;
104
+ @inject ( MonacoThemingService )
105
+ private readonly monacoTheme : MonacoThemingService ;
106
+
107
+ onStart ( ) : void {
108
+ this . hostedPlugin . didStart . then ( ( ) => this . cleanObsoleteThemes ( ) ) ;
109
+ }
110
+
111
+ private async cleanObsoleteThemes ( ) : Promise < void > {
112
+ const pluginContributedThemes = this . monacoTheme . pluginContributedThemeIds ;
113
+ const persistedThemes = await getThemesFromIndexedDB ( ) ;
114
+ // if neither registered by code (e.g. webpack load such as the Arduino Theme) or VSIX, remove from the indexedDB.
115
+ const obsoleteThemes = persistedThemes . filter (
116
+ ( { id } ) =>
117
+ ! pluginContributedThemes . includes ( id ) && ! compiledThemeIds . has ( id )
118
+ ) ;
119
+ if ( ! obsoleteThemes . length ) {
120
+ return ;
121
+ }
122
+ await obsoleteThemes . reduce ( async ( previousTask , theme ) => {
123
+ await previousTask ;
124
+ return deleteThemeFromIndexedDB ( theme . id ) ;
125
+ } , Promise . resolve ( ) ) ;
126
+ }
27
127
}
0 commit comments