Update menu when reassigning keybindings or installing keybinding extension (fixes #14625)

This commit is contained in:
Benjamin Pasero committed 2017-01-07 14:32:18 +01:00
1 parent 4f88ca70c8
commit db859bf0eb
3 files changed
+109 -78

No files matched your search

+1 -2
View File
@@ -261,8 +261,7 @@ function main(accessor: ServicesAccessor, mainIpcServer: Server, userEnv: platfo
}
// Install Menu
const menu = instantiationService2.createInstance(VSCodeMenu);
menu.ready();
instantiationService2.createInstance(VSCodeMenu);
});
}
+101 -76
View File
@@ -21,6 +21,10 @@ import { Keybinding } from 'vs/base/common/keyCodes';
import { KeybindingLabels } from 'vs/base/common/keybinding';
import product from 'vs/platform/node/product';
import { RunOnceScheduler } from 'vs/base/common/async';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import Event, { Emitter, once } from 'vs/base/common/event';
import { ConfigWatcher } from 'vs/base/node/config';
import { IUserFriendlyKeybinding } from 'vs/platform/keybinding/common/keybinding';
interface IResolvedKeybinding {
id: string;
@@ -46,10 +50,97 @@ interface IConfiguration extends IFilesConfiguration {
};
}
export class VSCodeMenu {
class KeybindingsResolver {
private static lastKnownKeybindingsMapStorageKey = 'lastKnownKeybindings';
private commandIds: Set<string>;
private keybindings: { [commandId: string]: string };
private keybindingsWatcher: ConfigWatcher<IUserFriendlyKeybinding[]>;
private _onKeybindingsChanged = new Emitter<void>();
onKeybindingsChanged: Event<void> = this._onKeybindingsChanged.event;
constructor(
@IStorageService private storageService: IStorageService,
@IEnvironmentService environmentService: IEnvironmentService,
@IWindowsMainService private windowsService: IWindowsMainService
) {
this.commandIds = new Set<string>();
this.keybindings = this.storageService.getItem<{ [id: string]: string; }>(KeybindingsResolver.lastKnownKeybindingsMapStorageKey) || Object.create(null);
this.keybindingsWatcher = new ConfigWatcher<IUserFriendlyKeybinding[]>(environmentService.appKeybindingsPath, { changeBufferDelay: 1000 /* update after 1s */ });
this.registerListeners();
}
private registerListeners(): void {
// Resolve keybindings when any first window is loaded
const onceOnWindowReady = once(this.windowsService.onWindowReady);
onceOnWindowReady(win => this.resolveKeybindings(win));
// Listen to resolved keybindings from window
ipc.on('vscode:keybindingsResolved', (event, rawKeybindings: string) => {
let keybindings: IResolvedKeybinding[] = [];
try {
keybindings = JSON.parse(rawKeybindings);
} catch (error) {
// Should not happen
}
// Fill hash map of resolved keybindings and check for changes
let keybindingsChanged = false;
let keybindingsCount = 0;
keybindings.forEach(keybinding => {
const accelerator = KeybindingLabels._toElectronAccelerator(new Keybinding(keybinding.binding));
if (accelerator) {
keybindingsCount++;
if (accelerator !== this.keybindings[keybinding.id]) {
this.keybindings[keybinding.id] = accelerator;
keybindingsChanged = true;
}
}
});
// A keybinding might have been unassigned, so we have to account for that too
if (Object.keys(this.keybindings).length !== keybindingsCount) {
keybindingsChanged = true;
}
if (keybindingsChanged) {
this.storageService.setItem(KeybindingsResolver.lastKnownKeybindingsMapStorageKey, this.keybindings); // keep to restore instantly after restart
this._onKeybindingsChanged.fire();
}
});
// Resolve keybindings again when keybindings.json changes
this.keybindingsWatcher.onDidUpdateConfiguration(() => this.resolveKeybindings());
// Resolve keybindings when window reloads because an installed extension could have an impact
this.windowsService.onWindowReload(() => this.resolveKeybindings());
}
private resolveKeybindings(win: VSCodeWindow = this.windowsService.getLastActiveWindow()): void {
if (this.commandIds.size && win) {
const commandIds = [];
this.commandIds.forEach(id => commandIds.push(id));
win.sendWhenReady('vscode:resolveKeybindings', JSON.stringify(commandIds));
}
}
public getKeybinding(commandId: string): string {
if (!this.commandIds.has(commandId)) {
this.commandIds.add(commandId);
}
return this.keybindings[commandId];
}
}
export class VSCodeMenu {
private static MAX_MENU_RECENT_ENTRIES = 10;
private currentAutoSaveSetting: string;
@@ -62,35 +153,28 @@ export class VSCodeMenu {
private menuUpdater: RunOnceScheduler;
private actionIdKeybindingRequests: string[];
private mapLastKnownKeybindingToActionId: { [id: string]: string; };
private mapResolvedKeybindingToActionId: { [id: string]: string; };
private keybindingsResolved: boolean;
private keybindingsResolver: KeybindingsResolver;
private extensionViewlets: IExtensionViewlet[];
constructor(
@IStorageService private storageService: IStorageService,
@IUpdateService private updateService: IUpdateService,
@IInstantiationService instantiationService: IInstantiationService,
@IConfigurationService private configurationService: IConfigurationService,
@IWindowsMainService private windowsService: IWindowsMainService,
@IEnvironmentService private environmentService: IEnvironmentService,
@ITelemetryService private telemetryService: ITelemetryService
) {
this.actionIdKeybindingRequests = [];
this.extensionViewlets = [];
this.mapResolvedKeybindingToActionId = Object.create(null);
this.mapLastKnownKeybindingToActionId = this.storageService.getItem<{ [id: string]: string; }>(VSCodeMenu.lastKnownKeybindingsMapStorageKey) || Object.create(null);
this.menuUpdater = new RunOnceScheduler(() => this.doUpdateMenu(), 0);
this.keybindingsResolver = instantiationService.createInstance(KeybindingsResolver);
this.onConfigurationUpdated(this.configurationService.getConfiguration<IConfiguration>());
}
public ready(): void {
this.registerListeners();
this.install();
this.registerListeners();
}
private registerListeners(): void {
@@ -105,43 +189,6 @@ export class VSCodeMenu {
this.windowsService.onRecentPathsChange(paths => this.updateMenu());
this.windowsService.onWindowClose(_ => this.onClose(this.windowsService.getWindowCount()));
// Resolve keybindings when any first workbench is loaded
this.windowsService.onWindowReady(win => this.resolveKeybindings(win));
// Listen to resolved keybindings
ipc.on('vscode:keybindingsResolved', (event, rawKeybindings) => {
let keybindings: IResolvedKeybinding[] = [];
try {
keybindings = JSON.parse(rawKeybindings);
} catch (error) {
// Should not happen
}
// Fill hash map of resolved keybindings
let needsMenuUpdate = false;
keybindings.forEach(keybinding => {
const accelerator = KeybindingLabels._toElectronAccelerator(new Keybinding(keybinding.binding));
if (accelerator) {
this.mapResolvedKeybindingToActionId[keybinding.id] = accelerator;
if (this.mapLastKnownKeybindingToActionId[keybinding.id] !== accelerator) {
needsMenuUpdate = true; // we only need to update when something changed!
}
}
});
// A keybinding might have been unassigned, so we have to account for that too
if (Object.keys(this.mapLastKnownKeybindingToActionId).length !== Object.keys(this.mapResolvedKeybindingToActionId).length) {
needsMenuUpdate = true;
}
if (needsMenuUpdate) {
this.storageService.setItem(VSCodeMenu.lastKnownKeybindingsMapStorageKey, this.mapResolvedKeybindingToActionId); // keep to restore instantly after restart
this.mapLastKnownKeybindingToActionId = this.mapResolvedKeybindingToActionId; // update our last known map
this.updateMenu();
}
});
// Listen to extension viewlets
ipc.on('vscode:extensionViewlets', (event, rawExtensionViewlets) => {
let extensionViewlets: IExtensionViewlet[] = [];
@@ -162,6 +209,9 @@ export class VSCodeMenu {
// Listen to update service
this.updateService.onStateChange(() => this.updateMenu());
// Listen to keybindings change
this.keybindingsResolver.onKeybindingsChanged(() => this.updateMenu());
}
private onConfigurationUpdated(config: IConfiguration, handleMenu?: boolean): void {
@@ -201,19 +251,6 @@ export class VSCodeMenu {
}
}
private resolveKeybindings(win: VSCodeWindow): void {
if (this.keybindingsResolved) {
return; // only resolve once
}
this.keybindingsResolved = true;
// Resolve keybindings when workbench window is up
if (this.actionIdKeybindingRequests.length) {
win.send('vscode:resolveKeybindings', JSON.stringify(this.actionIdKeybindingRequests));
}
}
private updateMenu(): void {
this.menuUpdater.schedule(); // buffer multiple attempts to update the menu
}
@@ -970,19 +1007,7 @@ export class VSCodeMenu {
private getAccelerator(actionId: string, fallback?: string): string {
if (actionId) {
const resolvedKeybinding = this.mapResolvedKeybindingToActionId[actionId];
if (resolvedKeybinding) {
return resolvedKeybinding; // keybinding is fully resolved
}
if (!this.keybindingsResolved) {
this.actionIdKeybindingRequests.push(actionId); // keybinding needs to be resolved
}
const lastKnownKeybinding = this.mapLastKnownKeybindingToActionId[actionId];
if (lastKnownKeybinding) {
return lastKnownKeybinding; // return the last known keybining (chance of mismatch is very low unless it changed)
}
return this.keybindingsResolver.getKeybinding(actionId);
}
return fallback;
+7
View File
@@ -104,6 +104,7 @@ export interface IWindowsMainService {
// events
onWindowReady: CommonEvent<VSCodeWindow>;
onWindowClose: CommonEvent<number>;
onWindowReload: CommonEvent<number>;
onPathsOpen: CommonEvent<IPath[]>;
onRecentPathsChange: CommonEvent<void>;
@@ -159,6 +160,9 @@ export class WindowsManager implements IWindowsMainService {
private _onWindowClose = new Emitter<number>();
onWindowClose: CommonEvent<number> = this._onWindowClose.event;
private _onWindowReload = new Emitter<number>();
onWindowReload: CommonEvent<number> = this._onWindowReload.event;
private _onPathsOpen = new Emitter<IPath[]>();
onPathsOpen: CommonEvent<IPath> = this._onPathsOpen.event;
@@ -292,6 +296,9 @@ export class WindowsManager implements IWindowsMainService {
this.lifecycleService.unload(win, UnloadReason.RELOAD).done(veto => {
if (!veto) {
win.reload(cli);
// Emit
this._onWindowReload.fire(win.id);
}
});
}