Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import * as platform from '../../../../base/common/platform.js';
7
import { IExtensionDescription, IExtension } from '../../../../platform/extensions/common/extensions.js';
8
import { dedupExtensions } from '../common/extensionsUtil.js';
9
import { IExtensionsScannerService, IScannedExtension, toExtensionDescription as toExtensionDescriptionFromScannedExtension } from '../../../../platform/extensionManagement/common/extensionsScannerService.js';
10
import { ILogService } from '../../../../platform/log/common/log.js';
11
import Severity from '../../../../base/common/severity.js';
12
import { localize } from '../../../../nls.js';
13
import { INotificationService } from '../../../../platform/notification/common/notification.js';
14
import { IHostService } from '../../host/browser/host.js';
15
import { timeout } from '../../../../base/common/async.js';
16
import { IUserDataProfileService } from '../../userDataProfile/common/userDataProfile.js';
17
import { getErrorMessage } from '../../../../base/common/errors.js';
18
import { IWorkbenchExtensionManagementService } from '../../extensionManagement/common/extensionManagement.js';
19
import { toExtensionDescription } from '../common/extensions.js';
20
import { IWorkbenchEnvironmentService } from '../../environment/common/environmentService.js';
21
22
export class CachedExtensionScanner {
23
24
public readonly scannedExtensions: Promise<IExtensionDescription[]>;
25
private _scannedExtensionsResolve!: (result: IExtensionDescription[]) => void;
26
private _scannedExtensionsReject!: (err: any) => void;
27
28
constructor(
29
@INotificationService private readonly _notificationService: INotificationService,
30
@IHostService private readonly _hostService: IHostService,
31
@IExtensionsScannerService private readonly _extensionsScannerService: IExtensionsScannerService,
32
@IUserDataProfileService private readonly _userDataProfileService: IUserDataProfileService,
33
@IWorkbenchExtensionManagementService private readonly _extensionManagementService: IWorkbenchExtensionManagementService,
34
@IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService,
35
@ILogService private readonly _logService: ILogService,
36
) {
37
this.scannedExtensions = new Promise<IExtensionDescription[]>((resolve, reject) => {
38
this._scannedExtensionsResolve = resolve;
39
this._scannedExtensionsReject = reject;
40
});
41
}
42
43
public async startScanningExtensions(): Promise<void> {
44
try {
45
const extensions = await this._scanInstalledExtensions();
46
this._scannedExtensionsResolve(extensions);
47
} catch (err) {
48
this._scannedExtensionsReject(err);
49
}
50
}
51
52
private async _scanInstalledExtensions(): Promise<IExtensionDescription[]> {
53
try {
54
const language = platform.language;
55
const result = await Promise.allSettled([
56
this._extensionsScannerService.scanSystemExtensions({ language, checkControlFile: true }),
57
this._extensionsScannerService.scanUserExtensions({ language, profileLocation: this._userDataProfileService.currentProfile.extensionsResource, useCache: true }),
58
this._environmentService.remoteAuthority ? [] : this._extensionManagementService.getInstalledWorkspaceExtensions(false)
59
]);
60
61
let hasErrors = false;
62
63
let scannedSystemExtensions: IScannedExtension[] = [];
64
if (result[0].status === 'fulfilled') {
65
scannedSystemExtensions = result[0].value;
66
} else {
67
hasErrors = true;
68
this._logService.error(`Error scanning system extensions:`, getErrorMessage(result[0].reason));
69
}
70
71
let scannedUserExtensions: IScannedExtension[] = [];
72
if (result[1].status === 'fulfilled') {
73
scannedUserExtensions = result[1].value;
74
} else {
75
hasErrors = true;
76
this._logService.error(`Error scanning user extensions:`, getErrorMessage(result[1].reason));
77
}
78
79
let workspaceExtensions: IExtension[] = [];
80
if (result[2].status === 'fulfilled') {
81
workspaceExtensions = result[2].value;
82
} else {
83
hasErrors = true;
84
this._logService.error(`Error scanning workspace extensions:`, getErrorMessage(result[2].reason));
85
}
86
87
const scannedDevelopedExtensions: IScannedExtension[] = [];
88
try {
89
const allScannedDevelopedExtensions = await this._extensionsScannerService.scanExtensionsUnderDevelopment([...scannedSystemExtensions, ...scannedUserExtensions], { language, includeInvalid: true });
90
const invalidExtensions: IScannedExtension[] = [];
91
for (const extensionUnderDevelopment of allScannedDevelopedExtensions) {
92
if (extensionUnderDevelopment.isValid) {
93
scannedDevelopedExtensions.push(extensionUnderDevelopment);
94
} else {
95
invalidExtensions.push(extensionUnderDevelopment);
96
}
97
}
98
if (invalidExtensions.length > 0) {
99
this._notificationService.prompt(
100
Severity.Warning,
101
invalidExtensions.length === 1
102
? localize('extensionUnderDevelopment.invalid', "Failed loading extension '{0}' under development because it is invalid: {1}", invalidExtensions[0].location.fsPath, invalidExtensions[0].validations[0][1])
103
: localize('extensionsUnderDevelopment.invalid', "Failed loading extensions {0} under development because they are invalid: {1}", invalidExtensions.map(ext => `'${ext.location.fsPath}'`).join(', '), invalidExtensions.map(ext => `${ext.validations[0][1]}`).join(', ')),
104
[]
105
);
106
}
107
} catch (error) {
108
this._logService.error(error);
109
}
110
111
const system = scannedSystemExtensions.map(e => toExtensionDescriptionFromScannedExtension(e, false));
112
const user = scannedUserExtensions.map(e => toExtensionDescriptionFromScannedExtension(e, false));
113
const workspace = workspaceExtensions.map(e => toExtensionDescription(e, false));
114
const development = scannedDevelopedExtensions.map(e => toExtensionDescriptionFromScannedExtension(e, true));
115
const r = dedupExtensions(system, user, workspace, development, this._logService);
116
117
if (!hasErrors) {
118
const disposable = this._extensionsScannerService.onDidChangeCache(() => {
119
disposable.dispose();
120
this._notificationService.prompt(
121
Severity.Error,
122
localize('extensionCache.invalid', "Extensions have been modified on disk. Please reload the window."),
123
[{
124
label: localize('reloadWindow', "Reload Window"),
125
run: () => this._hostService.reload()
126
}]
127
);
128
});
129
timeout(5000).then(() => disposable.dispose());
130
}
131
132
return r;
133
} catch (err) {
134
this._logService.error(`Error scanning installed extensions:`);
135
this._logService.error(err);
136
return [];
137
}
138
}
139
140
}
141
142