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