3
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
4
*--------------------------------------------------------------------------------------------*/
5
5
6
- import { Disposable } from 'vs/base/common/lifecycle' ;
6
+ import { Disposable , DisposableStore , MutableDisposable , toDisposable } from 'vs/base/common/lifecycle' ;
7
7
import { URI } from 'vs/base/common/uri' ;
8
8
import { IExtensionGalleryService , IExtensionIdentifier , IGlobalExtensionEnablementService , ServerDidUninstallExtensionEvent , ServerInstallExtensionResult , UninstallOptions } from 'vs/platform/extensionManagement/common/extensionManagement' ;
9
+ import { getIdAndVersion } from 'vs/platform/extensionManagement/common/extensionManagementUtil' ;
9
10
import { IExtensionsProfileScannerService } from 'vs/platform/extensionManagement/common/extensionsProfileScannerService' ;
10
11
import { ExtensionStorageService , IExtensionStorageService } from 'vs/platform/extensionManagement/common/extensionStorage' ;
11
12
import { migrateUnsupportedExtensions } from 'vs/platform/extensionManagement/common/unsupportedExtensionsMigration' ;
@@ -44,7 +45,8 @@ export class ExtensionsCleaner extends Disposable {
44
45
class ProfileExtensionsCleaner extends Disposable {
45
46
46
47
private profileExtensionsLocations = new Map < string , URI [ ] > ;
47
- private initPromise : Promise < boolean > ;
48
+
49
+ private readonly profileModeDisposables = this . _register ( new MutableDisposable < DisposableStore > ( ) ) ;
48
50
49
51
constructor (
50
52
@INativeServerExtensionManagementService private readonly extensionManagementService : INativeServerExtensionManagementService ,
@@ -54,47 +56,54 @@ class ProfileExtensionsCleaner extends Disposable {
54
56
@ILogService private readonly logService : ILogService ,
55
57
) {
56
58
super ( ) ;
57
-
58
- this . initPromise = this . initialize ( ) ;
59
- this . _register ( this . userDataProfilesService . onDidChangeProfiles ( e => this . onDidChangeProfiles ( e ) ) ) ;
60
- this . _register ( this . extensionManagementService . onDidInstallExtensions ( e => this . onDidInstallExtensions ( e ) ) ) ;
61
- this . _register ( this . extensionManagementService . onDidUninstallExtension ( e => this . onDidUninstallExtension ( e ) ) ) ;
59
+ this . onDidChangeProfiles ( { added : this . userDataProfilesService . profiles , removed : [ ] , all : this . userDataProfilesService . profiles } ) ;
62
60
}
63
61
64
- private async initialize ( ) : Promise < boolean > {
65
- if ( this . userDataProfilesService . profiles . length === 1 ) {
66
- return true ;
62
+ private async onDidChangeProfiles ( { added, removed, all } : DidChangeProfilesEvent ) : Promise < void > {
63
+ try {
64
+ await Promise . all ( removed . map ( profile => profile . extensionsResource ? this . removeExtensionsFromProfile ( profile . extensionsResource ) : Promise . resolve ( ) ) ) ;
65
+ } catch ( error ) {
66
+ this . logService . error ( error ) ;
67
67
}
68
+
69
+ if ( all . length === 0 ) {
70
+ // Exit profile mode
71
+ this . profileModeDisposables . clear ( ) ;
72
+ // Listen for entering into profile mode
73
+ const disposable = this . _register ( this . userDataProfilesService . onDidChangeProfiles ( ( ) => {
74
+ disposable . dispose ( ) ;
75
+ this . onDidChangeProfiles ( { added : this . userDataProfilesService . profiles , removed : [ ] , all : this . userDataProfilesService . profiles } ) ;
76
+ } ) ) ;
77
+ return ;
78
+ }
79
+
68
80
try {
69
- const installed = await this . extensionManagementService . getAllUserInstalled ( ) ;
70
- await Promise . all ( this . userDataProfilesService . profiles . map ( profile => profile . extensionsResource ? this . populateExtensionsFromProfile ( profile . extensionsResource ) : Promise . resolve ( ) ) ) ;
71
- const toUninstall = installed . filter ( installedExtension => ! this . profileExtensionsLocations . has ( this . getKey ( installedExtension . identifier , installedExtension . manifest . version ) ) ) ;
72
- if ( toUninstall . length ) {
73
- await Promise . all ( toUninstall . map ( extension => this . extensionManagementService . uninstall ( extension , uninstalOptions ) ) ) ;
81
+ if ( added . length ) {
82
+ await Promise . all ( added . map ( profile => profile . extensionsResource ? this . populateExtensionsFromProfile ( profile . extensionsResource ) : Promise . resolve ( ) ) ) ;
83
+ // Enter profile mode
84
+ if ( ! this . profileModeDisposables . value ) {
85
+ this . profileModeDisposables . value = new DisposableStore ( ) ;
86
+ this . profileModeDisposables . value . add ( toDisposable ( ( ) => this . profileExtensionsLocations . clear ( ) ) ) ;
87
+ this . profileModeDisposables . value . add ( this . userDataProfilesService . onDidChangeProfiles ( e => this . onDidChangeProfiles ( e ) ) ) ;
88
+ this . profileModeDisposables . value . add ( this . extensionManagementService . onDidInstallExtensions ( e => this . onDidInstallExtensions ( e ) ) ) ;
89
+ this . profileModeDisposables . value . add ( this . extensionManagementService . onDidUninstallExtension ( e => this . onDidUninstallExtension ( e ) ) ) ;
90
+ await this . uninstallExtensionsNotInProfiles ( ) ;
91
+ }
74
92
}
75
- return true ;
76
93
} catch ( error ) {
77
- this . logService . error ( 'ExtensionsCleaner: Failed to initialize' ) ;
78
94
this . logService . error ( error ) ;
79
- return false ;
80
95
}
81
96
}
82
97
83
- private async onDidChangeProfiles ( { added, removed, all } : DidChangeProfilesEvent ) : Promise < void > {
84
- if ( ! ( await this . initPromise ) ) {
85
- return ;
86
- }
87
- await Promise . all ( added . map ( profile => profile . extensionsResource ? this . populateExtensionsFromProfile ( profile . extensionsResource ) : Promise . resolve ( ) ) ) ;
88
- await Promise . all ( removed . map ( profile => profile . extensionsResource ? this . removeExtensionsFromProfile ( profile . extensionsResource ) : Promise . resolve ( ) ) ) ;
89
- if ( all . length === 1 ) {
90
- this . profileExtensionsLocations . clear ( ) ;
98
+ private async uninstallExtensionsNotInProfiles ( ) : Promise < void > {
99
+ const installed = await this . extensionManagementService . getAllUserInstalled ( ) ;
100
+ const toUninstall = installed . filter ( installedExtension => ! this . profileExtensionsLocations . has ( this . getKey ( installedExtension . identifier , installedExtension . manifest . version ) ) ) ;
101
+ if ( toUninstall . length ) {
102
+ await Promise . all ( toUninstall . map ( extension => this . extensionManagementService . uninstall ( extension , uninstalOptions ) ) ) ;
91
103
}
92
104
}
93
105
94
106
private async onDidInstallExtensions ( installedExtensions : readonly ServerInstallExtensionResult [ ] ) : Promise < void > {
95
- if ( ! ( await this . initPromise ) ) {
96
- return ;
97
- }
98
107
for ( const { local, profileLocation } of installedExtensions ) {
99
108
if ( ! local || ! profileLocation ) {
100
109
continue ;
@@ -107,9 +116,6 @@ class ProfileExtensionsCleaner extends Disposable {
107
116
if ( ! e . profileLocation || ! e . version ) {
108
117
return ;
109
118
}
110
- if ( ! ( await this . initPromise ) ) {
111
- return ;
112
- }
113
119
if ( this . removeExtensionWithKey ( this . getKey ( e . identifier , e . version ) , e . profileLocation ) ) {
114
120
await this . uninstallExtensions ( [ { identifier : e . identifier , version : e . version } ] ) ;
115
121
}
@@ -123,8 +129,16 @@ class ProfileExtensionsCleaner extends Disposable {
123
129
}
124
130
125
131
private async removeExtensionsFromProfile ( removedProfile : URI ) : Promise < void > {
126
- const profileExtensions = await this . extensionsProfileScannerService . scanProfileExtensions ( removedProfile ) ;
127
- const extensionsToRemove = profileExtensions . filter ( profileExtension => this . removeExtensionWithKey ( this . getKey ( profileExtension . identifier , profileExtension . version ) , removedProfile ) ) ;
132
+ const extensionsToRemove : { identifier : IExtensionIdentifier ; version : string } [ ] = [ ] ;
133
+ for ( const key of [ ...this . profileExtensionsLocations . keys ( ) ] ) {
134
+ if ( ! this . removeExtensionWithKey ( key , removedProfile ) ) {
135
+ continue ;
136
+ }
137
+ const extensionToRemove = this . fromKey ( key ) ;
138
+ if ( extensionToRemove ) {
139
+ extensionsToRemove . push ( extensionToRemove ) ;
140
+ }
141
+ }
128
142
if ( extensionsToRemove . length ) {
129
143
await this . uninstallExtensions ( extensionsToRemove ) ;
130
144
}
@@ -149,8 +163,9 @@ class ProfileExtensionsCleaner extends Disposable {
149
163
}
150
164
if ( ! profiles ?. length ) {
151
165
this . profileExtensionsLocations . delete ( key ) ;
166
+ return true ;
152
167
}
153
- return ! profiles ?. length ;
168
+ return false ;
154
169
}
155
170
156
171
private async uninstallExtensions ( extensionsToRemove : { identifier : IExtensionIdentifier ; version : string } [ ] ) : Promise < void > {
@@ -165,4 +180,9 @@ class ProfileExtensionsCleaner extends Disposable {
165
180
return `${ ExtensionIdentifier . toKey ( identifier . id ) } @${ version } ` ;
166
181
}
167
182
183
+ private fromKey ( key : string ) : { identifier : IExtensionIdentifier ; version : string } | undefined {
184
+ const [ id , version ] = getIdAndVersion ( key ) ;
185
+ return version ? { identifier : { id } , version } : undefined ;
186
+ }
187
+
168
188
}
0 commit comments