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
5255 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 } 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
interface NotebookKeymapMemento {
47
[hasRecommendedKeymapKey]?: boolean;
48
}
49
50
export class NotebookKeymapService extends Disposable implements INotebookKeymapService {
51
_serviceBrand: undefined;
52
53
private notebookKeymapMemento: Memento<NotebookKeymapMemento>;
54
private notebookKeymap: NotebookKeymapMemento;
55
56
constructor(
57
@IInstantiationService private readonly instantiationService: IInstantiationService,
58
@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService,
59
@INotificationService private readonly notificationService: INotificationService,
60
@IStorageService storageService: IStorageService,
61
@ILifecycleService lifecycleService: ILifecycleService,
62
) {
63
super();
64
65
this.notebookKeymapMemento = new Memento('notebookKeymap', storageService);
66
this.notebookKeymap = this.notebookKeymapMemento.getMemento(StorageScope.PROFILE, StorageTarget.USER);
67
68
this._register(lifecycleService.onDidShutdown(() => this.dispose()));
69
this._register(this.instantiationService.invokeFunction(onExtensionChanged)((identifiers => {
70
Promise.all(identifiers.map(identifier => this.checkForOtherKeymaps(identifier)))
71
.then(undefined, onUnexpectedError);
72
})));
73
}
74
75
private checkForOtherKeymaps(extensionIdentifier: IExtensionIdentifier): Promise<void> {
76
return this.instantiationService.invokeFunction(getInstalledExtensions).then(extensions => {
77
const keymaps = extensions.filter(extension => isNotebookKeymapExtension(extension));
78
const extension = keymaps.find(extension => areSameExtensions(extension.identifier, extensionIdentifier));
79
if (extension && extension.globallyEnabled) {
80
// there is already a keymap extension
81
this.notebookKeymap[hasRecommendedKeymapKey] = true;
82
this.notebookKeymapMemento.saveMemento();
83
const otherKeymaps = keymaps.filter(extension => !areSameExtensions(extension.identifier, extensionIdentifier) && extension.globallyEnabled);
84
if (otherKeymaps.length) {
85
return this.promptForDisablingOtherKeymaps(extension, otherKeymaps);
86
}
87
}
88
return undefined;
89
});
90
}
91
92
private promptForDisablingOtherKeymaps(newKeymap: IExtensionStatus, oldKeymaps: IExtensionStatus[]): void {
93
const onPrompt = (confirmed: boolean) => {
94
if (confirmed) {
95
this.extensionEnablementService.setEnablement(oldKeymaps.map(keymap => keymap.local), EnablementState.DisabledGlobally);
96
}
97
};
98
99
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(', ')),
100
[{
101
label: localize('yes', "Yes"),
102
run: () => onPrompt(true)
103
}, {
104
label: localize('no', "No"),
105
run: () => onPrompt(false)
106
}]
107
);
108
}
109
}
110
111
export function isNotebookKeymapExtension(extension: IExtensionStatus): boolean {
112
if (extension.local.manifest.extensionPack) {
113
return false;
114
}
115
116
const keywords = extension.local.manifest.keywords;
117
if (!keywords) {
118
return false;
119
}
120
121
return keywords.indexOf('notebook-keymap') !== -1;
122
}
123
124