Files
vscode/src/vs/workbench/browser/actions/developerActions.ts
T

742 lines
28 KiB
TypeScript

/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./media/actions';
import { localize } from 'vs/nls';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { DomEmitter } from 'vs/base/browser/event';
import { Color } from 'vs/base/common/color';
import { Emitter, Event } from 'vs/base/common/event';
import { IDisposable, toDisposable, dispose, DisposableStore, setDisposableTracker, DisposableTracker, DisposableInfo } from 'vs/base/common/lifecycle';
import { getDomNodePagePosition, createStyleSheet, createCSSRule, append, $, getActiveDocument, onDidRegisterWindow, getWindows } from 'vs/base/browser/dom';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ContextKeyExpr, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { Context } from 'vs/platform/contextkey/browser/contextKeyService';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { RunOnceScheduler } from 'vs/base/common/async';
import { ILayoutService } from 'vs/platform/layout/browser/layoutService';
import { Registry } from 'vs/platform/registry/common/platform';
import { registerAction2, Action2, MenuRegistry } from 'vs/platform/actions/common/actions';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { clamp } from 'vs/base/common/numbers';
import { KeyCode } from 'vs/base/common/keyCodes';
import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry';
import { ILogService } from 'vs/platform/log/common/log';
import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { Categories } from 'vs/platform/action/common/actionCommonCategories';
import { IWorkingCopyBackupService } from 'vs/workbench/services/workingCopy/common/workingCopyBackup';
import { ResolutionResult, ResultKind } from 'vs/platform/keybinding/common/keybindingResolver';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IOutputService } from 'vs/workbench/services/output/common/output';
import { windowLogId } from 'vs/workbench/services/log/common/logConstants';
import { ByteSize } from 'vs/platform/files/common/files';
import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import product from 'vs/platform/product/common/product';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
class InspectContextKeysAction extends Action2 {
constructor() {
super({
id: 'workbench.action.inspectContextKeys',
title: { value: localize('inspect context keys', "Inspect Context Keys"), original: 'Inspect Context Keys' },
category: Categories.Developer,
f1: true
});
}
run(accessor: ServicesAccessor): void {
const contextKeyService = accessor.get(IContextKeyService);
const disposables = new DisposableStore();
const stylesheet = createStyleSheet(undefined, undefined, disposables);
createCSSRule('*', 'cursor: crosshair !important;', stylesheet);
const hoverFeedback = document.createElement('div');
const activeDocument = getActiveDocument();
activeDocument.body.appendChild(hoverFeedback);
disposables.add(toDisposable(() => activeDocument.body.removeChild(hoverFeedback)));
hoverFeedback.style.position = 'absolute';
hoverFeedback.style.pointerEvents = 'none';
hoverFeedback.style.backgroundColor = 'rgba(255, 0, 0, 0.5)';
hoverFeedback.style.zIndex = '1000';
const onMouseMove = disposables.add(new DomEmitter(activeDocument, 'mousemove', true));
disposables.add(onMouseMove.event(e => {
const target = e.target as HTMLElement;
const position = getDomNodePagePosition(target);
hoverFeedback.style.top = `${position.top}px`;
hoverFeedback.style.left = `${position.left}px`;
hoverFeedback.style.width = `${position.width}px`;
hoverFeedback.style.height = `${position.height}px`;
}));
const onMouseDown = disposables.add(new DomEmitter(activeDocument, 'mousedown', true));
Event.once(onMouseDown.event)(e => { e.preventDefault(); e.stopPropagation(); }, null, disposables);
const onMouseUp = disposables.add(new DomEmitter(activeDocument, 'mouseup', true));
Event.once(onMouseUp.event)(e => {
e.preventDefault();
e.stopPropagation();
const context = contextKeyService.getContext(e.target as HTMLElement) as Context;
console.log(context.collectAllValues());
dispose(disposables);
}, null, disposables);
}
}
interface IScreencastKeyboardOptions {
readonly showKeys?: boolean;
readonly showKeybindings?: boolean;
readonly showCommands?: boolean;
readonly showCommandGroups?: boolean;
readonly showSingleEditorCursorMoves?: boolean;
}
class ToggleScreencastModeAction extends Action2 {
static disposable: IDisposable | undefined;
constructor() {
super({
id: 'workbench.action.toggleScreencastMode',
title: { value: localize('toggle screencast mode', "Toggle Screencast Mode"), original: 'Toggle Screencast Mode' },
category: Categories.Developer,
f1: true
});
}
run(accessor: ServicesAccessor): void {
if (ToggleScreencastModeAction.disposable) {
ToggleScreencastModeAction.disposable.dispose();
ToggleScreencastModeAction.disposable = undefined;
return;
}
const layoutService = accessor.get(ILayoutService);
const configurationService = accessor.get(IConfigurationService);
const keybindingService = accessor.get(IKeybindingService);
const disposables = new DisposableStore();
const container = layoutService.activeContainer;
const mouseMarker = append(container, $('.screencast-mouse'));
disposables.add(toDisposable(() => mouseMarker.remove()));
const keyboardMarker = append(container, $('.screencast-keyboard'));
disposables.add(toDisposable(() => keyboardMarker.remove()));
const onMouseDown = disposables.add(new Emitter<MouseEvent>());
const onMouseUp = disposables.add(new Emitter<MouseEvent>());
const onMouseMove = disposables.add(new Emitter<MouseEvent>());
function registerContainerListeners(container: HTMLElement, disposables: DisposableStore): void {
disposables.add(disposables.add(new DomEmitter(container, 'mousedown', true)).event(e => onMouseDown.fire(e)));
disposables.add(disposables.add(new DomEmitter(container, 'mouseup', true)).event(e => onMouseUp.fire(e)));
disposables.add(disposables.add(new DomEmitter(container, 'mousemove', true)).event(e => onMouseMove.fire(e)));
}
for (const { window, disposables } of getWindows()) {
registerContainerListeners(layoutService.getContainer(window), disposables);
}
disposables.add(onDidRegisterWindow(({ window, disposables }) => registerContainerListeners(layoutService.getContainer(window), disposables)));
disposables.add(layoutService.onDidChangeActiveContainer(() => {
layoutService.activeContainer.appendChild(mouseMarker);
layoutService.activeContainer.appendChild(keyboardMarker);
}));
const updateMouseIndicatorColor = () => {
mouseMarker.style.borderColor = Color.fromHex(configurationService.getValue<string>('screencastMode.mouseIndicatorColor')).toString();
};
let mouseIndicatorSize: number;
const updateMouseIndicatorSize = () => {
mouseIndicatorSize = clamp(configurationService.getValue<number>('screencastMode.mouseIndicatorSize') || 20, 20, 100);
mouseMarker.style.height = `${mouseIndicatorSize}px`;
mouseMarker.style.width = `${mouseIndicatorSize}px`;
};
updateMouseIndicatorColor();
updateMouseIndicatorSize();
disposables.add(onMouseDown.event(e => {
mouseMarker.style.top = `${e.clientY - mouseIndicatorSize / 2}px`;
mouseMarker.style.left = `${e.clientX - mouseIndicatorSize / 2}px`;
mouseMarker.style.display = 'block';
mouseMarker.style.transform = `scale(${1})`;
mouseMarker.style.transition = 'transform 0.1s';
const mouseMoveListener = onMouseMove.event(e => {
mouseMarker.style.top = `${e.clientY - mouseIndicatorSize / 2}px`;
mouseMarker.style.left = `${e.clientX - mouseIndicatorSize / 2}px`;
mouseMarker.style.transform = `scale(${.8})`;
});
Event.once(onMouseUp.event)(() => {
mouseMarker.style.display = 'none';
mouseMoveListener.dispose();
});
}));
const updateKeyboardFontSize = () => {
keyboardMarker.style.fontSize = `${clamp(configurationService.getValue<number>('screencastMode.fontSize') || 56, 20, 100)}px`;
};
const updateKeyboardMarker = () => {
keyboardMarker.style.bottom = `${clamp(configurationService.getValue<number>('screencastMode.verticalOffset') || 0, 0, 90)}%`;
};
let keyboardMarkerTimeout!: number;
const updateKeyboardMarkerTimeout = () => {
keyboardMarkerTimeout = clamp(configurationService.getValue<number>('screencastMode.keyboardOverlayTimeout') || 800, 500, 5000);
};
updateKeyboardFontSize();
updateKeyboardMarker();
updateKeyboardMarkerTimeout();
disposables.add(configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration('screencastMode.verticalOffset')) {
updateKeyboardMarker();
}
if (e.affectsConfiguration('screencastMode.fontSize')) {
updateKeyboardFontSize();
}
if (e.affectsConfiguration('screencastMode.keyboardOverlayTimeout')) {
updateKeyboardMarkerTimeout();
}
if (e.affectsConfiguration('screencastMode.mouseIndicatorColor')) {
updateMouseIndicatorColor();
}
if (e.affectsConfiguration('screencastMode.mouseIndicatorSize')) {
updateMouseIndicatorSize();
}
}));
const onKeyDown = disposables.add(new Emitter<KeyboardEvent>());
const onCompositionStart = disposables.add(new Emitter<CompositionEvent>());
const onCompositionUpdate = disposables.add(new Emitter<CompositionEvent>());
const onCompositionEnd = disposables.add(new Emitter<CompositionEvent>());
function registerWindowListeners(window: Window, disposables: DisposableStore): void {
disposables.add(disposables.add(new DomEmitter(window, 'keydown', true)).event(e => onKeyDown.fire(e)));
disposables.add(disposables.add(new DomEmitter(window, 'compositionstart', true)).event(e => onCompositionStart.fire(e)));
disposables.add(disposables.add(new DomEmitter(window, 'compositionupdate', true)).event(e => onCompositionUpdate.fire(e)));
disposables.add(disposables.add(new DomEmitter(window, 'compositionend', true)).event(e => onCompositionEnd.fire(e)));
}
for (const { window, disposables } of getWindows()) {
registerWindowListeners(window, disposables);
}
disposables.add(onDidRegisterWindow(({ window, disposables }) => registerWindowListeners(window, disposables)));
let length = 0;
let composing: Element | undefined = undefined;
let imeBackSpace = false;
const clearKeyboardScheduler = new RunOnceScheduler(() => {
keyboardMarker.textContent = '';
composing = undefined;
length = 0;
}, keyboardMarkerTimeout);
disposables.add(onCompositionStart.event(e => {
imeBackSpace = true;
}));
disposables.add(onCompositionUpdate.event(e => {
if (e.data && imeBackSpace) {
if (length > 20) {
keyboardMarker.innerText = '';
length = 0;
}
composing = composing ?? append(keyboardMarker, $('span.key'));
composing.textContent = e.data;
} else if (imeBackSpace) {
keyboardMarker.innerText = '';
append(keyboardMarker, $('span.key', {}, `Backspace`));
}
clearKeyboardScheduler.schedule();
}));
disposables.add(onCompositionEnd.event(e => {
composing = undefined;
length++;
}));
disposables.add(onKeyDown.event(e => {
if (e.key === 'Process' || /[\uac00-\ud787\u3131-\u314e\u314f-\u3163\u3041-\u3094\u30a1-\u30f4\u30fc\u3005\u3006\u3024\u4e00-\u9fa5]/u.test(e.key)) {
if (e.code === 'Backspace') {
imeBackSpace = true;
} else if (!e.code.includes('Key')) {
composing = undefined;
imeBackSpace = false;
} else {
imeBackSpace = true;
}
clearKeyboardScheduler.schedule();
return;
}
if (e.isComposing) {
return;
}
const options = configurationService.getValue<IScreencastKeyboardOptions>('screencastMode.keyboardOptions');
const event = new StandardKeyboardEvent(e);
const shortcut = keybindingService.softDispatch(event, event.target);
// Hide the single arrow key pressed
if (shortcut.kind === ResultKind.KbFound && shortcut.commandId && !(options.showSingleEditorCursorMoves ?? true) && (
['cursorLeft', 'cursorRight', 'cursorUp', 'cursorDown'].includes(shortcut.commandId))
) {
return;
}
if (
event.ctrlKey || event.altKey || event.metaKey || event.shiftKey
|| length > 20
|| event.keyCode === KeyCode.Backspace || event.keyCode === KeyCode.Escape
|| event.keyCode === KeyCode.UpArrow || event.keyCode === KeyCode.DownArrow
|| event.keyCode === KeyCode.LeftArrow || event.keyCode === KeyCode.RightArrow
) {
keyboardMarker.innerText = '';
length = 0;
}
const keybinding = keybindingService.resolveKeyboardEvent(event);
const commandDetails = (this._isKbFound(shortcut) && shortcut.commandId) ? this.getCommandDetails(shortcut.commandId) : undefined;
let commandAndGroupLabel = commandDetails?.title;
let keyLabel: string | undefined | null = keybinding.getLabel();
if (commandDetails) {
if ((options.showCommandGroups ?? false) && commandDetails.category) {
commandAndGroupLabel = `${commandDetails.category}: ${commandAndGroupLabel} `;
}
if (this._isKbFound(shortcut) && shortcut.commandId) {
const keybindings = keybindingService.lookupKeybindings(shortcut.commandId)
.filter(k => k.getLabel()?.endsWith(keyLabel ?? ''));
if (keybindings.length > 0) {
keyLabel = keybindings[keybindings.length - 1].getLabel();
}
}
}
if ((options.showCommands ?? true) && commandAndGroupLabel) {
append(keyboardMarker, $('span.title', {}, `${commandAndGroupLabel} `));
}
if ((options.showKeys ?? true) || (commandDetails && (options.showKeybindings ?? true))) {
// Fix label for arrow keys
keyLabel = keyLabel?.replace('UpArrow', '↑')
?.replace('DownArrow', '↓')
?.replace('LeftArrow', '←')
?.replace('RightArrow', '→');
append(keyboardMarker, $('span.key', {}, keyLabel ?? ''));
}
length++;
clearKeyboardScheduler.schedule();
}));
ToggleScreencastModeAction.disposable = disposables;
}
private _isKbFound(resolutionResult: ResolutionResult): resolutionResult is { kind: ResultKind.KbFound; commandId: string | null; commandArgs: any; isBubble: boolean } {
return resolutionResult.kind === ResultKind.KbFound;
}
private getCommandDetails(commandId: string): { title: string; category?: string } | undefined {
const fromMenuRegistry = MenuRegistry.getCommand(commandId);
if (fromMenuRegistry) {
return {
title: typeof fromMenuRegistry.title === 'string' ? fromMenuRegistry.title : fromMenuRegistry.title.value,
category: fromMenuRegistry.category ? (typeof fromMenuRegistry.category === 'string' ? fromMenuRegistry.category : fromMenuRegistry.category.value) : undefined
};
}
const fromCommandsRegistry = CommandsRegistry.getCommand(commandId);
if (fromCommandsRegistry && fromCommandsRegistry.metadata?.description) {
return { title: typeof fromCommandsRegistry.metadata.description === 'string' ? fromCommandsRegistry.metadata.description : fromCommandsRegistry.metadata.description.value };
}
return undefined;
}
}
class LogStorageAction extends Action2 {
constructor() {
super({
id: 'workbench.action.logStorage',
title: { value: localize({ key: 'logStorage', comment: ['A developer only action to log the contents of the storage for the current window.'] }, "Log Storage Database Contents"), original: 'Log Storage Database Contents' },
category: Categories.Developer,
f1: true
});
}
run(accessor: ServicesAccessor): void {
const storageService = accessor.get(IStorageService);
const dialogService = accessor.get(IDialogService);
storageService.log();
dialogService.info(localize('storageLogDialogMessage', "The storage database contents have been logged to the developer tools."), localize('storageLogDialogDetails', "Open developer tools from the menu and select the Console tab."));
}
}
class LogWorkingCopiesAction extends Action2 {
constructor() {
super({
id: 'workbench.action.logWorkingCopies',
title: { value: localize({ key: 'logWorkingCopies', comment: ['A developer only action to log the working copies that exist.'] }, "Log Working Copies"), original: 'Log Working Copies' },
category: Categories.Developer,
f1: true
});
}
async run(accessor: ServicesAccessor): Promise<void> {
const workingCopyService = accessor.get(IWorkingCopyService);
const workingCopyBackupService = accessor.get(IWorkingCopyBackupService);
const logService = accessor.get(ILogService);
const outputService = accessor.get(IOutputService);
const backups = await workingCopyBackupService.getBackups();
const msg = [
``,
`[Working Copies]`,
...(workingCopyService.workingCopies.length > 0) ?
workingCopyService.workingCopies.map(workingCopy => `${workingCopy.isDirty() ? '● ' : ''}${workingCopy.resource.toString(true)} (typeId: ${workingCopy.typeId || '<no typeId>'})`) :
['<none>'],
``,
`[Backups]`,
...(backups.length > 0) ?
backups.map(backup => `${backup.resource.toString(true)} (typeId: ${backup.typeId || '<no typeId>'})`) :
['<none>'],
];
logService.info(msg.join('\n'));
outputService.showChannel(windowLogId, true);
}
}
class RemoveLargeStorageEntriesAction extends Action2 {
private static SIZE_THRESHOLD = 1024 * 16; // 16kb
constructor() {
super({
id: 'workbench.action.removeLargeStorageDatabaseEntries',
title: { value: localize('removeLargeStorageDatabaseEntries', "Remove Large Storage Database Entries..."), original: 'Remove Large Storage Database Entries...' },
category: Categories.Developer,
f1: true
});
}
async run(accessor: ServicesAccessor): Promise<void> {
const storageService = accessor.get(IStorageService);
const quickInputService = accessor.get(IQuickInputService);
const userDataProfileService = accessor.get(IUserDataProfileService);
const dialogService = accessor.get(IDialogService);
interface IStorageItem extends IQuickPickItem {
readonly key: string;
readonly scope: StorageScope;
readonly target: StorageTarget;
readonly size: number;
}
const items: IStorageItem[] = [];
for (const scope of [StorageScope.APPLICATION, StorageScope.PROFILE, StorageScope.WORKSPACE]) {
if (scope === StorageScope.PROFILE && userDataProfileService.currentProfile.isDefault) {
continue; // avoid duplicates
}
for (const target of [StorageTarget.MACHINE, StorageTarget.USER]) {
for (const key of storageService.keys(scope, target)) {
const value = storageService.get(key, scope);
if (value && value.length > RemoveLargeStorageEntriesAction.SIZE_THRESHOLD) {
items.push({
key,
scope,
target,
size: value.length,
label: key,
description: ByteSize.formatSize(value.length),
detail: localize('largeStorageItemDetail', "Scope: {0}, Target: {1}", scope === StorageScope.APPLICATION ? localize('global', "Global") : scope === StorageScope.PROFILE ? localize('profile', "Profile") : localize('workspace', "Workspace"), target === StorageTarget.MACHINE ? localize('machine', "Machine") : localize('user', "User")),
});
}
}
}
}
items.sort((itemA, itemB) => itemB.size - itemA.size);
const selectedItems = await new Promise<readonly IStorageItem[]>(resolve => {
const disposables = new DisposableStore();
const picker = disposables.add(quickInputService.createQuickPick<IStorageItem>());
picker.items = items;
picker.canSelectMany = true;
picker.ok = false;
picker.customButton = true;
picker.hideCheckAll = true;
picker.customLabel = localize('removeLargeStorageEntriesPickerButton', "Remove");
picker.placeholder = localize('removeLargeStorageEntriesPickerPlaceholder', "Select large entries to remove from storage");
if (items.length === 0) {
picker.description = localize('removeLargeStorageEntriesPickerDescriptionNoEntries', "There are no large storage entries to remove.");
}
picker.show();
disposables.add(picker.onDidCustom(() => {
resolve(picker.selectedItems);
picker.hide();
}));
disposables.add(picker.onDidHide(() => disposables.dispose()));
});
if (selectedItems.length === 0) {
return;
}
const { confirmed } = await dialogService.confirm({
type: 'warning',
message: localize('removeLargeStorageEntriesConfirmRemove', "Do you want to remove the selected storage entries from the database?"),
detail: localize('removeLargeStorageEntriesConfirmRemoveDetail', "{0}\n\nThis action is irreversible and may result in data loss!", selectedItems.map(item => item.label).join('\n')),
primaryButton: localize({ key: 'removeLargeStorageEntriesButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Remove")
});
if (!confirmed) {
return;
}
const scopesToOptimize = new Set<StorageScope>();
for (const item of selectedItems) {
storageService.remove(item.key, item.scope);
scopesToOptimize.add(item.scope);
}
for (const scope of scopesToOptimize) {
await storageService.optimize(scope);
}
}
}
let tracker: DisposableTracker | undefined = undefined;
let trackedDisposables = new Set<IDisposable>();
const DisposablesSnapshotStateContext = new RawContextKey<'started' | 'pending' | 'stopped'>('dirtyWorkingCopies', 'stopped');
class StartTrackDisposables extends Action2 {
constructor() {
super({
id: 'workbench.action.startTrackDisposables',
title: { value: localize('startTrackDisposables', "Start Tracking Disposables"), original: 'Start Tracking Disposables' },
category: Categories.Developer,
f1: true,
precondition: ContextKeyExpr.and(DisposablesSnapshotStateContext.isEqualTo('pending').negate(), DisposablesSnapshotStateContext.isEqualTo('started').negate())
});
}
run(accessor: ServicesAccessor): void {
const disposablesSnapshotStateContext = DisposablesSnapshotStateContext.bindTo(accessor.get(IContextKeyService));
disposablesSnapshotStateContext.set('started');
trackedDisposables.clear();
tracker = new DisposableTracker();
setDisposableTracker(tracker);
}
}
class SnapshotTrackedDisposables extends Action2 {
constructor() {
super({
id: 'workbench.action.snapshotTrackedDisposables',
title: { value: localize('snapshotTrackedDisposables', "Snapshot Tracked Disposables"), original: 'Snapshot Tracked Disposables' },
category: Categories.Developer,
f1: true,
precondition: DisposablesSnapshotStateContext.isEqualTo('started')
});
}
run(accessor: ServicesAccessor): void {
const disposablesSnapshotStateContext = DisposablesSnapshotStateContext.bindTo(accessor.get(IContextKeyService));
disposablesSnapshotStateContext.set('pending');
trackedDisposables = new Set(tracker?.computeLeakingDisposables(1000)?.leaks.map(disposable => disposable.value));
}
}
class StopTrackDisposables extends Action2 {
constructor() {
super({
id: 'workbench.action.stopTrackDisposables',
title: { value: localize('stopTrackDisposables', "Stop Tracking Disposables"), original: 'Stop Tracking Disposables' },
category: Categories.Developer,
f1: true,
precondition: DisposablesSnapshotStateContext.isEqualTo('pending')
});
}
run(accessor: ServicesAccessor): void {
const editorService = accessor.get(IEditorService);
const disposablesSnapshotStateContext = DisposablesSnapshotStateContext.bindTo(accessor.get(IContextKeyService));
disposablesSnapshotStateContext.set('stopped');
if (tracker) {
const disposableLeaks = new Set<DisposableInfo>();
for (const disposable of new Set(tracker.computeLeakingDisposables(1000)?.leaks) ?? []) {
if (trackedDisposables.has(disposable.value)) {
disposableLeaks.add(disposable);
}
}
const leaks = tracker.computeLeakingDisposables(1000, Array.from(disposableLeaks));
if (leaks) {
editorService.openEditor({ resource: undefined, contents: leaks.details });
}
}
setDisposableTracker(null);
tracker = undefined;
trackedDisposables.clear();
}
}
// --- Actions Registration
registerAction2(InspectContextKeysAction);
registerAction2(ToggleScreencastModeAction);
registerAction2(LogStorageAction);
registerAction2(LogWorkingCopiesAction);
registerAction2(RemoveLargeStorageEntriesAction);
if (!product.commit) {
registerAction2(StartTrackDisposables);
registerAction2(SnapshotTrackedDisposables);
registerAction2(StopTrackDisposables);
}
// --- Configuration
// Screen Cast Mode
const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration);
configurationRegistry.registerConfiguration({
id: 'screencastMode',
order: 9,
title: localize('screencastModeConfigurationTitle', "Screencast Mode"),
type: 'object',
properties: {
'screencastMode.verticalOffset': {
type: 'number',
default: 20,
minimum: 0,
maximum: 90,
description: localize('screencastMode.location.verticalPosition', "Controls the vertical offset of the screencast mode overlay from the bottom as a percentage of the workbench height.")
},
'screencastMode.fontSize': {
type: 'number',
default: 56,
minimum: 20,
maximum: 100,
description: localize('screencastMode.fontSize', "Controls the font size (in pixels) of the screencast mode keyboard.")
},
'screencastMode.keyboardOptions': {
type: 'object',
description: localize('screencastMode.keyboardOptions.description', "Options for customizing the keyboard overlay in screencast mode."),
properties: {
'showKeys': {
type: 'boolean',
default: true,
description: localize('screencastMode.keyboardOptions.showKeys', "Show raw keys.")
},
'showKeybindings': {
type: 'boolean',
default: true,
description: localize('screencastMode.keyboardOptions.showKeybindings', "Show keyboard shortcuts.")
},
'showCommands': {
type: 'boolean',
default: true,
description: localize('screencastMode.keyboardOptions.showCommands', "Show command names.")
},
'showCommandGroups': {
type: 'boolean',
default: false,
description: localize('screencastMode.keyboardOptions.showCommandGroups', "Show command group names, when commands are also shown.")
},
'showSingleEditorCursorMoves': {
type: 'boolean',
default: true,
description: localize('screencastMode.keyboardOptions.showSingleEditorCursorMoves', "Show single editor cursor move commands.")
}
},
default: {
'showKeys': true,
'showKeybindings': true,
'showCommands': true,
'showCommandGroups': false,
'showSingleEditorCursorMoves': true
},
additionalProperties: false
},
'screencastMode.keyboardOverlayTimeout': {
type: 'number',
default: 800,
minimum: 500,
maximum: 5000,
description: localize('screencastMode.keyboardOverlayTimeout', "Controls how long (in milliseconds) the keyboard overlay is shown in screencast mode.")
},
'screencastMode.mouseIndicatorColor': {
type: 'string',
format: 'color-hex',
default: '#FF0000',
description: localize('screencastMode.mouseIndicatorColor', "Controls the color in hex (#RGB, #RGBA, #RRGGBB or #RRGGBBAA) of the mouse indicator in screencast mode.")
},
'screencastMode.mouseIndicatorSize': {
type: 'number',
default: 20,
minimum: 20,
maximum: 100,
description: localize('screencastMode.mouseIndicatorSize', "Controls the size (in pixels) of the mouse indicator in screencast mode.")
},
}
});