Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/extensions/common/extensionsUtils.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 { localize } from '../../../../nls.js';
7
import { Event } from '../../../../base/common/event.js';
8
import { onUnexpectedError } from '../../../../base/common/errors.js';
9
import { Disposable } from '../../../../base/common/lifecycle.js';
10
import { IExtensionManagementService, ILocalExtension, IExtensionIdentifier, InstallOperation } from '../../../../platform/extensionManagement/common/extensionManagement.js';
11
import { IWorkbenchExtensionEnablementService, EnablementState } from '../../../services/extensionManagement/common/extensionManagement.js';
12
import { IExtensionRecommendationsService } from '../../../services/extensionRecommendations/common/extensionRecommendations.js';
13
import { ILifecycleService } from '../../../services/lifecycle/common/lifecycle.js';
14
import { IWorkbenchContribution } from '../../../common/contributions.js';
15
import { ServicesAccessor, IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
16
import { areSameExtensions } from '../../../../platform/extensionManagement/common/extensionManagementUtil.js';
17
import { Severity, INotificationService } from '../../../../platform/notification/common/notification.js';
18
19
export interface IExtensionStatus {
20
identifier: IExtensionIdentifier;
21
local: ILocalExtension;
22
globallyEnabled: boolean;
23
}
24
25
export class KeymapExtensions extends Disposable implements IWorkbenchContribution {
26
27
constructor(
28
@IInstantiationService private readonly instantiationService: IInstantiationService,
29
@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService,
30
@IExtensionRecommendationsService private readonly tipsService: IExtensionRecommendationsService,
31
@ILifecycleService lifecycleService: ILifecycleService,
32
@INotificationService private readonly notificationService: INotificationService,
33
) {
34
super();
35
this._register(lifecycleService.onDidShutdown(() => this.dispose()));
36
this._register(instantiationService.invokeFunction(onExtensionChanged)((identifiers => {
37
Promise.all(identifiers.map(identifier => this.checkForOtherKeymaps(identifier)))
38
.then(undefined, onUnexpectedError);
39
})));
40
}
41
42
private checkForOtherKeymaps(extensionIdentifier: IExtensionIdentifier): Promise<void> {
43
return this.instantiationService.invokeFunction(getInstalledExtensions).then(extensions => {
44
const keymaps = extensions.filter(extension => isKeymapExtension(this.tipsService, extension));
45
const extension = keymaps.find(extension => areSameExtensions(extension.identifier, extensionIdentifier));
46
if (extension && extension.globallyEnabled) {
47
const otherKeymaps = keymaps.filter(extension => !areSameExtensions(extension.identifier, extensionIdentifier) && extension.globallyEnabled);
48
if (otherKeymaps.length) {
49
return this.promptForDisablingOtherKeymaps(extension, otherKeymaps);
50
}
51
}
52
return undefined;
53
});
54
}
55
56
private promptForDisablingOtherKeymaps(newKeymap: IExtensionStatus, oldKeymaps: IExtensionStatus[]): void {
57
const onPrompt = (confirmed: boolean) => {
58
if (confirmed) {
59
this.extensionEnablementService.setEnablement(oldKeymaps.map(keymap => keymap.local), EnablementState.DisabledGlobally);
60
}
61
};
62
63
this.notificationService.prompt(Severity.Info, localize('disableOtherKeymapsConfirmation', "Disable other keymaps ({0}) to avoid conflicts between keybindings?", oldKeymaps.map(k => `'${k.local.manifest.displayName}'`).join(', ')),
64
[{
65
label: localize('yes', "Yes"),
66
run: () => onPrompt(true)
67
}, {
68
label: localize('no', "No"),
69
run: () => onPrompt(false)
70
}]
71
);
72
}
73
}
74
75
function onExtensionChanged(accessor: ServicesAccessor): Event<IExtensionIdentifier[]> {
76
const extensionService = accessor.get(IExtensionManagementService);
77
const extensionEnablementService = accessor.get(IWorkbenchExtensionEnablementService);
78
const onDidInstallExtensions = Event.chain(extensionService.onDidInstallExtensions, $ =>
79
$.filter(e => e.some(({ operation }) => operation === InstallOperation.Install))
80
.map(e => e.map(({ identifier }) => identifier))
81
);
82
return Event.debounce<IExtensionIdentifier[], IExtensionIdentifier[]>(Event.any(
83
Event.any(onDidInstallExtensions, Event.map(extensionService.onDidUninstallExtension, e => [e.identifier])),
84
Event.map(extensionEnablementService.onEnablementChanged, extensions => extensions.map(e => e.identifier))
85
), (result: IExtensionIdentifier[] | undefined, identifiers: IExtensionIdentifier[]) => {
86
result = result || [];
87
for (const identifier of identifiers) {
88
if (result.some(l => !areSameExtensions(l, identifier))) {
89
result.push(identifier);
90
}
91
}
92
return result;
93
});
94
}
95
96
export async function getInstalledExtensions(accessor: ServicesAccessor): Promise<IExtensionStatus[]> {
97
const extensionService = accessor.get(IExtensionManagementService);
98
const extensionEnablementService = accessor.get(IWorkbenchExtensionEnablementService);
99
const extensions = await extensionService.getInstalled();
100
return extensions.map(extension => {
101
return {
102
identifier: extension.identifier,
103
local: extension,
104
globallyEnabled: extensionEnablementService.isEnabled(extension)
105
};
106
});
107
}
108
109
function isKeymapExtension(tipsService: IExtensionRecommendationsService, extension: IExtensionStatus): boolean {
110
const cats = extension.local.manifest.categories;
111
return cats && cats.indexOf('Keymaps') !== -1 || tipsService.getKeymapRecommendations().some(extensionId => areSameExtensions({ id: extensionId }, extension.local.identifier));
112
}
113
114