Extract keymaps code from ExtensionsWorkbenchService (fixes #16493)

This commit is contained in:
Christof Marti 2017-02-07 15:58:16 -08:00
parent 38fbba35ff
commit b46834dd50
3 changed files with 115 additions and 49 deletions

View file

@ -37,14 +37,16 @@ import jsonContributionRegistry = require('vs/platform/jsonschemas/common/jsonCo
import { ExtensionsConfigurationSchema, ExtensionsConfigurationSchemaId } from 'vs/workbench/parts/extensions/common/extensionsFileTemplate';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { KeymapExtensions } from 'vs/workbench/parts/extensions/electron-browser/keymapExtensions';
// Singletons
registerSingleton(IExtensionGalleryService, ExtensionGalleryService);
registerSingleton(IExtensionTipsService, ExtensionTipsService);
registerSingleton(IExtensionsWorkbenchService, ExtensionsWorkbenchService);
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench)
.registerWorkbenchContribution(StatusUpdater);
const workbenchRegistry = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench);
workbenchRegistry.registerWorkbenchContribution(StatusUpdater);
workbenchRegistry.registerWorkbenchContribution(KeymapExtensions);
Registry.as<IOutputChannelRegistry>(OutputExtensions.OutputChannels)
.registerChannel(ExtensionsChannelId, ExtensionsLabel);

View file

@ -0,0 +1,110 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import arrays = require('vs/base/common/arrays');
import nls = require('vs/nls');
import { chain, any } from 'vs/base/common/event';
import { onUnexpectedError, canceled } from 'vs/base/common/errors';
import { TPromise } from 'vs/base/common/winjs.base';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IExtensionManagementService, ILocalExtension, IExtensionEnablementService, IExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IChoiceService } from 'vs/platform/message/common/message';
import Severity from 'vs/base/common/severity';
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
interface IExtension {
identifier: string;
local: ILocalExtension;
}
export class KeymapExtensions implements IWorkbenchContribution {
private disposables: IDisposable[] = [];
constructor(
@IExtensionManagementService private extensionService: IExtensionManagementService,
@IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService,
@IExtensionTipsService private tipsService: IExtensionTipsService,
@IChoiceService private choiceService: IChoiceService,
@ILifecycleService lifecycleService: ILifecycleService,
@ITelemetryService private telemetryService: ITelemetryService,
) {
this.disposables.push(
lifecycleService.onShutdown(() => this.dispose()),
any(
chain(extensionService.onDidInstallExtension)
.map(e => stripVersion(e.id))
.event,
extensionEnablementService.onEnablementChanged
)((id => {
this.checkForOtherKeymaps(id)
.then(null, onUnexpectedError);
}))
);
}
getId(): string {
return 'vs.extensions.keymapExtensions';
}
private checkForOtherKeymaps(extensionId: string): TPromise<void> {
return this.extensionService.getInstalled().then(extensions => {
const installedExtensions = extensions.map(ext => ({ identifier: stripVersion(ext.id), local: ext }));
const extension = arrays.first(installedExtensions, ext => ext.identifier === extensionId);
const globallyDisabled = this.extensionEnablementService.getGloballyDisabledExtensions();
if (extension && this.isKeymapExtension(extension) && globallyDisabled.indexOf(extensionId) === -1) {
const otherKeymaps = installedExtensions.filter(ext => ext.identifier !== extensionId &&
this.isKeymapExtension(ext) &&
globallyDisabled.indexOf(ext.identifier) === -1);
if (otherKeymaps.length) {
return this.promptForDisablingOtherKeymaps(extension, otherKeymaps);
}
}
return undefined;
});
}
private isKeymapExtension(extension: IExtension): boolean {
const cats = extension.local.manifest.categories;
return cats && cats.indexOf('Keymaps') !== -1 || this.tipsService.getKeymapRecommendations().indexOf(extension.identifier) !== -1;
}
private promptForDisablingOtherKeymaps(newKeymap: IExtension, oldKeymaps: IExtension[]): TPromise<void> {
const telemetryData: { [key: string]: any; } = {
newKeymap: newKeymap.identifier,
oldKeymaps: oldKeymaps.map(k => k.identifier)
};
this.telemetryService.publicLog('disableOtherKeymapsConfirmation', telemetryData);
const message = nls.localize('disableOtherKeymapsConfirmation', "Disable other keymaps to avoid conflicts between keybindings?");
const options = [
nls.localize('yes', "Yes"),
nls.localize('no', "No")
];
return this.choiceService.choose(Severity.Info, message, options, false)
.then<void>(value => {
const confirmed = value === 0;
telemetryData['confirmed'] = confirmed;
this.telemetryService.publicLog('disableOtherKeymaps', telemetryData);
if (confirmed) {
return TPromise.join(oldKeymaps.map(keymap => {
return this.extensionEnablementService.setEnablement(keymap.identifier, false);
}));
}
return undefined;
}, error => TPromise.wrapError(canceled()));
}
dispose(): void {
this.disposables = dispose(this.disposables);
}
}
function stripVersion(id: string): string {
return id.replace(/-\d+\.\d+\.\d+$/, '');
}

View file

@ -15,7 +15,7 @@ import { LinkedMap as Map } from 'vs/base/common/map';
import { assign } from 'vs/base/common/objects';
import { isUUID } from 'vs/base/common/uuid';
import { ThrottledDelayer } from 'vs/base/common/async';
import { isPromiseCanceledError, onUnexpectedError, canceled } from 'vs/base/common/errors';
import { isPromiseCanceledError } from 'vs/base/common/errors';
import { TPromise } from 'vs/base/common/winjs.base';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IPager, mapPager, singlePagePager } from 'vs/base/common/paging';
@ -689,8 +689,6 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService {
installed.local = local;
} else {
this.installed.push(extension);
this.checkForOtherKeymaps(extension)
.then(null, onUnexpectedError);
}
}
if (extension.gallery) {
@ -701,48 +699,6 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService {
this._onChange.fire();
}
private checkForOtherKeymaps(extension: Extension): TPromise<void> {
if (!extension.disabledGlobally && this.isKeymapExtension(extension)) {
const otherKeymaps = this.installed.filter(ext => ext.identifier !== extension.identifier &&
!ext.disabledGlobally &&
this.isKeymapExtension(ext));
if (otherKeymaps.length) {
return this.promptForDisablingOtherKeymaps(extension, otherKeymaps);
}
}
return TPromise.as(undefined);
}
private isKeymapExtension(extension: Extension): boolean {
const cats = extension.local.manifest.categories;
return cats && cats.indexOf('Keymaps') !== -1 || this.tipsService.getKeymapRecommendations().indexOf(extension.identifier) !== -1;
}
private promptForDisablingOtherKeymaps(newKeymap: Extension, oldKeymaps: Extension[]): TPromise<void> {
const telemetryData: { [key: string]: any; } = {
newKeymap: newKeymap.identifier,
oldKeymaps: oldKeymaps.map(k => k.identifier)
};
this.telemetryService.publicLog('disableOtherKeymapsConfirmation', telemetryData);
const message = nls.localize('disableOtherKeymapsConfirmation', "Disable other keymaps to avoid conflicts between keybindings?");
const options = [
nls.localize('yes', "Yes"),
nls.localize('no', "No")
];
return this.choiceService.choose(Severity.Info, message, options, false)
.then<void>(value => {
const confirmed = value === 0;
telemetryData['confirmed'] = confirmed;
this.telemetryService.publicLog('disableOtherKeymaps', telemetryData);
if (confirmed) {
return TPromise.join(oldKeymaps.map(keymap => {
return this.setEnablement(keymap, false);
}));
}
return undefined;
}, error => TPromise.wrapError(canceled()));
}
private onUninstallExtension(id: string): void {
const extension = this.installed.filter(e => e.local.id === id)[0];
const newLength = this.installed.filter(e => e.local.id !== id).length;
@ -785,8 +741,6 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService {
extension.disabledGlobally = globallyDisabledExtensions.indexOf(extension.identifier) !== -1;
extension.disabledForWorkspace = workspaceDisabledExtensions.indexOf(extension.identifier) !== -1;
this._onChange.fire();
this.checkForOtherKeymaps(<Extension>extension)
.then(null, onUnexpectedError);
}
}