mirror of
https://github.com/coder/code-server.git
synced 2026-05-09 05:47:26 +02:00
Merge commit 'be3e8236086165e5e45a5a10783823874b3f3ebd' as 'lib/vscode'
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import * as nls from 'vs/nls';
|
||||
import { INativeHostService } from 'vs/platform/native/electron-sandbox/native';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
|
||||
export class ToggleDevToolsAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.toggleDevTools';
|
||||
static readonly LABEL = nls.localize('toggleDevTools', "Toggle Developer Tools");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@INativeHostService private readonly nativeHostService: INativeHostService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(): Promise<void> {
|
||||
return this.nativeHostService.toggleDevTools();
|
||||
}
|
||||
}
|
||||
|
||||
export class ConfigureRuntimeArgumentsAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.configureRuntimeArguments';
|
||||
static readonly LABEL = nls.localize('configureRuntimeArguments', "Configure Runtime Arguments");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IEnvironmentService private readonly environmentService: IEnvironmentService,
|
||||
@IEditorService private readonly editorService: IEditorService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
async run(): Promise<void> {
|
||||
await this.editorService.openEditor({ resource: this.environmentService.argvResource });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-workbench .quick-input-list .quick-input-list-entry.has-actions:hover .quick-input-list-entry-action-bar .action-label.dirty-window::before {
|
||||
content: "\ea76"; /* Close icon flips between black dot and "X" for dirty windows */
|
||||
}
|
||||
@@ -0,0 +1,274 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { URI } from 'vs/base/common/uri';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import * as nls from 'vs/nls';
|
||||
import { applyZoom } from 'vs/platform/windows/electron-sandbox/window';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { getZoomLevel } from 'vs/base/browser/browser';
|
||||
import { FileKind } from 'vs/platform/files/common/files';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { IQuickInputService, IQuickInputButton } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { getIconClasses } from 'vs/editor/common/services/getIconClasses';
|
||||
import { ICommandHandler } from 'vs/platform/commands/common/commands';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { INativeHostService } from 'vs/platform/native/electron-sandbox/native';
|
||||
import { Codicon } from 'vs/base/common/codicons';
|
||||
|
||||
export class CloseCurrentWindowAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.closeWindow';
|
||||
static readonly LABEL = nls.localize('closeWindow', "Close Window");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@INativeHostService private readonly nativeHostService: INativeHostService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
async run(): Promise<void> {
|
||||
this.nativeHostService.closeWindow();
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class BaseZoomAction extends Action {
|
||||
|
||||
private static readonly SETTING_KEY = 'window.zoomLevel';
|
||||
|
||||
private static readonly MAX_ZOOM_LEVEL = 9;
|
||||
private static readonly MIN_ZOOM_LEVEL = -8;
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
protected async setConfiguredZoomLevel(level: number): Promise<void> {
|
||||
level = Math.round(level); // when reaching smallest zoom, prevent fractional zoom levels
|
||||
|
||||
if (level > BaseZoomAction.MAX_ZOOM_LEVEL || level < BaseZoomAction.MIN_ZOOM_LEVEL) {
|
||||
return; // https://github.com/microsoft/vscode/issues/48357
|
||||
}
|
||||
|
||||
await this.configurationService.updateValue(BaseZoomAction.SETTING_KEY, level);
|
||||
|
||||
applyZoom(level);
|
||||
}
|
||||
}
|
||||
|
||||
export class ZoomInAction extends BaseZoomAction {
|
||||
|
||||
static readonly ID = 'workbench.action.zoomIn';
|
||||
static readonly LABEL = nls.localize('zoomIn', "Zoom In");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IConfigurationService configurationService: IConfigurationService
|
||||
) {
|
||||
super(id, label, configurationService);
|
||||
}
|
||||
|
||||
async run(): Promise<void> {
|
||||
this.setConfiguredZoomLevel(getZoomLevel() + 1);
|
||||
}
|
||||
}
|
||||
|
||||
export class ZoomOutAction extends BaseZoomAction {
|
||||
|
||||
static readonly ID = 'workbench.action.zoomOut';
|
||||
static readonly LABEL = nls.localize('zoomOut', "Zoom Out");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IConfigurationService configurationService: IConfigurationService
|
||||
) {
|
||||
super(id, label, configurationService);
|
||||
}
|
||||
|
||||
async run(): Promise<void> {
|
||||
this.setConfiguredZoomLevel(getZoomLevel() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
export class ZoomResetAction extends BaseZoomAction {
|
||||
|
||||
static readonly ID = 'workbench.action.zoomReset';
|
||||
static readonly LABEL = nls.localize('zoomReset', "Reset Zoom");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IConfigurationService configurationService: IConfigurationService
|
||||
) {
|
||||
super(id, label, configurationService);
|
||||
}
|
||||
|
||||
async run(): Promise<void> {
|
||||
this.setConfiguredZoomLevel(0);
|
||||
}
|
||||
}
|
||||
|
||||
export class ReloadWindowWithExtensionsDisabledAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.reloadWindowWithExtensionsDisabled';
|
||||
static readonly LABEL = nls.localize('reloadWindowWithExtensionsDisabled', "Reload With Extensions Disabled");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@INativeHostService private readonly nativeHostService: INativeHostService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
async run(): Promise<boolean> {
|
||||
await this.nativeHostService.reload({ disableExtensions: true });
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class BaseSwitchWindow extends Action {
|
||||
|
||||
private readonly closeWindowAction: IQuickInputButton = {
|
||||
iconClass: Codicon.removeClose.classNames,
|
||||
tooltip: nls.localize('close', "Close Window")
|
||||
};
|
||||
|
||||
private readonly closeDirtyWindowAction: IQuickInputButton = {
|
||||
iconClass: 'dirty-window ' + Codicon.closeDirty,
|
||||
tooltip: nls.localize('close', "Close Window"),
|
||||
alwaysVisible: true
|
||||
};
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
private readonly quickInputService: IQuickInputService,
|
||||
private readonly keybindingService: IKeybindingService,
|
||||
private readonly modelService: IModelService,
|
||||
private readonly modeService: IModeService,
|
||||
private readonly nativeHostService: INativeHostService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
protected abstract isQuickNavigate(): boolean;
|
||||
|
||||
async run(): Promise<void> {
|
||||
const currentWindowId = this.nativeHostService.windowId;
|
||||
|
||||
const windows = await this.nativeHostService.getWindows();
|
||||
const placeHolder = nls.localize('switchWindowPlaceHolder', "Select a window to switch to");
|
||||
const picks = windows.map(win => {
|
||||
const resource = win.filename ? URI.file(win.filename) : win.folderUri ? win.folderUri : win.workspace ? win.workspace.configPath : undefined;
|
||||
const fileKind = win.filename ? FileKind.FILE : win.workspace ? FileKind.ROOT_FOLDER : win.folderUri ? FileKind.FOLDER : FileKind.FILE;
|
||||
return {
|
||||
payload: win.id,
|
||||
label: win.title,
|
||||
ariaLabel: win.dirty ? nls.localize('windowDirtyAriaLabel', "{0}, dirty window", win.title) : win.title,
|
||||
iconClasses: getIconClasses(this.modelService, this.modeService, resource, fileKind),
|
||||
description: (currentWindowId === win.id) ? nls.localize('current', "Current Window") : undefined,
|
||||
buttons: currentWindowId !== win.id ? win.dirty ? [this.closeDirtyWindowAction] : [this.closeWindowAction] : undefined
|
||||
};
|
||||
});
|
||||
const autoFocusIndex = (picks.indexOf(picks.filter(pick => pick.payload === currentWindowId)[0]) + 1) % picks.length;
|
||||
|
||||
const pick = await this.quickInputService.pick(picks, {
|
||||
contextKey: 'inWindowsPicker',
|
||||
activeItem: picks[autoFocusIndex],
|
||||
placeHolder,
|
||||
quickNavigate: this.isQuickNavigate() ? { keybindings: this.keybindingService.lookupKeybindings(this.id) } : undefined,
|
||||
onDidTriggerItemButton: async context => {
|
||||
await this.nativeHostService.closeWindowById(context.item.payload);
|
||||
context.removeItem();
|
||||
}
|
||||
});
|
||||
|
||||
if (pick) {
|
||||
this.nativeHostService.focusWindow({ windowId: pick.payload });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class SwitchWindow extends BaseSwitchWindow {
|
||||
|
||||
static readonly ID = 'workbench.action.switchWindow';
|
||||
static readonly LABEL = nls.localize('switchWindow', "Switch Window...");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IQuickInputService quickInputService: IQuickInputService,
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IModelService modelService: IModelService,
|
||||
@IModeService modeService: IModeService,
|
||||
@INativeHostService nativeHostService: INativeHostService
|
||||
) {
|
||||
super(id, label, quickInputService, keybindingService, modelService, modeService, nativeHostService);
|
||||
}
|
||||
|
||||
protected isQuickNavigate(): boolean {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export class QuickSwitchWindow extends BaseSwitchWindow {
|
||||
|
||||
static readonly ID = 'workbench.action.quickSwitchWindow';
|
||||
static readonly LABEL = nls.localize('quickSwitchWindow', "Quick Switch Window...");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IQuickInputService quickInputService: IQuickInputService,
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IModelService modelService: IModelService,
|
||||
@IModeService modeService: IModeService,
|
||||
@INativeHostService nativeHostService: INativeHostService
|
||||
) {
|
||||
super(id, label, quickInputService, keybindingService, modelService, modeService, nativeHostService);
|
||||
}
|
||||
|
||||
protected isQuickNavigate(): boolean {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export const NewWindowTabHandler: ICommandHandler = function (accessor: ServicesAccessor) {
|
||||
return accessor.get(INativeHostService).newWindowTab();
|
||||
};
|
||||
|
||||
export const ShowPreviousWindowTabHandler: ICommandHandler = function (accessor: ServicesAccessor) {
|
||||
return accessor.get(INativeHostService).showPreviousWindowTab();
|
||||
};
|
||||
|
||||
export const ShowNextWindowTabHandler: ICommandHandler = function (accessor: ServicesAccessor) {
|
||||
return accessor.get(INativeHostService).showNextWindowTab();
|
||||
};
|
||||
|
||||
export const MoveWindowTabToNewWindowHandler: ICommandHandler = function (accessor: ServicesAccessor) {
|
||||
return accessor.get(INativeHostService).moveWindowTabToNewWindow();
|
||||
};
|
||||
|
||||
export const MergeWindowTabsHandlerHandler: ICommandHandler = function (accessor: ServicesAccessor) {
|
||||
return accessor.get(INativeHostService).mergeAllWindowTabs();
|
||||
};
|
||||
|
||||
export const ToggleWindowTabsBarHandler: ICommandHandler = function (accessor: ServicesAccessor) {
|
||||
return accessor.get(INativeHostService).toggleWindowTabsBar();
|
||||
};
|
||||
@@ -0,0 +1,400 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import * as nls from 'vs/nls';
|
||||
import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { IWorkbenchActionRegistry, Extensions, CATEGORIES } from 'vs/workbench/common/actions';
|
||||
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { isLinux, isMacintosh } from 'vs/base/common/platform';
|
||||
import { ToggleDevToolsAction, ConfigureRuntimeArgumentsAction } from 'vs/workbench/electron-sandbox/actions/developerActions';
|
||||
import { ZoomResetAction, ZoomOutAction, ZoomInAction, CloseCurrentWindowAction, SwitchWindow, QuickSwitchWindow, ReloadWindowWithExtensionsDisabledAction, NewWindowTabHandler, ShowPreviousWindowTabHandler, ShowNextWindowTabHandler, MoveWindowTabToNewWindowHandler, MergeWindowTabsHandlerHandler, ToggleWindowTabsBarHandler } from 'vs/workbench/electron-sandbox/actions/windowActions';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IsDevelopmentContext, IsMacContext } from 'vs/platform/contextkey/common/contextkeys';
|
||||
import { EditorsVisibleContext, SingleEditorGroupsContext } from 'vs/workbench/common/editor';
|
||||
import { INativeHostService } from 'vs/platform/native/electron-sandbox/native';
|
||||
import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
|
||||
import product from 'vs/platform/product/common/product';
|
||||
import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
||||
|
||||
// Actions
|
||||
(function registerActions(): void {
|
||||
const registry = Registry.as<IWorkbenchActionRegistry>(Extensions.WorkbenchActions);
|
||||
|
||||
// Actions: Zoom
|
||||
(function registerZoomActions(): void {
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.from(ZoomInAction, { primary: KeyMod.CtrlCmd | KeyCode.US_EQUAL, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_EQUAL, KeyMod.CtrlCmd | KeyCode.NUMPAD_ADD] }), 'View: Zoom In', CATEGORIES.View.value);
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.from(ZoomOutAction, { primary: KeyMod.CtrlCmd | KeyCode.US_MINUS, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_MINUS, KeyMod.CtrlCmd | KeyCode.NUMPAD_SUBTRACT], linux: { primary: KeyMod.CtrlCmd | KeyCode.US_MINUS, secondary: [KeyMod.CtrlCmd | KeyCode.NUMPAD_SUBTRACT] } }), 'View: Zoom Out', CATEGORIES.View.value);
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.from(ZoomResetAction, { primary: KeyMod.CtrlCmd | KeyCode.NUMPAD_0 }), 'View: Reset Zoom', CATEGORIES.View.value);
|
||||
})();
|
||||
|
||||
// Actions: Window
|
||||
(function registerWindowActions(): void {
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.from(CloseCurrentWindowAction, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_W }), 'Close Window');
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.from(SwitchWindow, { primary: 0, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_W } }), 'Switch Window...');
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.from(QuickSwitchWindow), 'Quick Switch Window...');
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: CloseCurrentWindowAction.ID, // close the window when the last editor is closed by reusing the same keybinding
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: ContextKeyExpr.and(EditorsVisibleContext.toNegated(), SingleEditorGroupsContext),
|
||||
primary: KeyMod.CtrlCmd | KeyCode.KEY_W,
|
||||
handler: accessor => {
|
||||
const nativeHostService = accessor.get(INativeHostService);
|
||||
nativeHostService.closeWindow();
|
||||
}
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'workbench.action.quit',
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
handler(accessor: ServicesAccessor) {
|
||||
const nativeHostService = accessor.get(INativeHostService);
|
||||
nativeHostService.quit();
|
||||
},
|
||||
when: undefined,
|
||||
mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_Q },
|
||||
linux: { primary: KeyMod.CtrlCmd | KeyCode.KEY_Q }
|
||||
});
|
||||
})();
|
||||
|
||||
// Actions: macOS Native Tabs
|
||||
(function registerMacOSNativeTabsActions(): void {
|
||||
if (isMacintosh) {
|
||||
[
|
||||
{ handler: NewWindowTabHandler, id: 'workbench.action.newWindowTab', title: { value: nls.localize('newTab', "New Window Tab"), original: 'New Window Tab' } },
|
||||
{ handler: ShowPreviousWindowTabHandler, id: 'workbench.action.showPreviousWindowTab', title: { value: nls.localize('showPreviousTab', "Show Previous Window Tab"), original: 'Show Previous Window Tab' } },
|
||||
{ handler: ShowNextWindowTabHandler, id: 'workbench.action.showNextWindowTab', title: { value: nls.localize('showNextWindowTab', "Show Next Window Tab"), original: 'Show Next Window Tab' } },
|
||||
{ handler: MoveWindowTabToNewWindowHandler, id: 'workbench.action.moveWindowTabToNewWindow', title: { value: nls.localize('moveWindowTabToNewWindow', "Move Window Tab to New Window"), original: 'Move Window Tab to New Window' } },
|
||||
{ handler: MergeWindowTabsHandlerHandler, id: 'workbench.action.mergeAllWindowTabs', title: { value: nls.localize('mergeAllWindowTabs', "Merge All Windows"), original: 'Merge All Windows' } },
|
||||
{ handler: ToggleWindowTabsBarHandler, id: 'workbench.action.toggleWindowTabsBar', title: { value: nls.localize('toggleWindowTabsBar', "Toggle Window Tabs Bar"), original: 'Toggle Window Tabs Bar' } }
|
||||
].forEach(command => {
|
||||
CommandsRegistry.registerCommand(command.id, command.handler);
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
|
||||
command,
|
||||
when: ContextKeyExpr.equals('config.window.nativeTabs', true)
|
||||
});
|
||||
});
|
||||
}
|
||||
})();
|
||||
|
||||
// Actions: Developer
|
||||
(function registerDeveloperActions(): void {
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.from(ReloadWindowWithExtensionsDisabledAction), 'Developer: Reload With Extensions Disabled', CATEGORIES.Developer.value);
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.from(ToggleDevToolsAction), 'Developer: Toggle Developer Tools', CATEGORIES.Developer.value);
|
||||
|
||||
KeybindingsRegistry.registerKeybindingRule({
|
||||
id: ToggleDevToolsAction.ID,
|
||||
weight: KeybindingWeight.WorkbenchContrib + 50,
|
||||
when: IsDevelopmentContext,
|
||||
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_I,
|
||||
mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_I }
|
||||
});
|
||||
})();
|
||||
|
||||
// Actions: Runtime Arguments
|
||||
(function registerRuntimeArgumentsAction(): void {
|
||||
const preferencesCategory = nls.localize('preferences', "Preferences");
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.from(ConfigureRuntimeArgumentsAction), 'Preferences: Configure Runtime Arguments', preferencesCategory);
|
||||
})();
|
||||
})();
|
||||
|
||||
// Menu
|
||||
(function registerMenu(): void {
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
|
||||
group: '6_close',
|
||||
command: {
|
||||
id: CloseCurrentWindowAction.ID,
|
||||
title: nls.localize({ key: 'miCloseWindow', comment: ['&& denotes a mnemonic'] }, "Clos&&e Window")
|
||||
},
|
||||
order: 4
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
|
||||
group: 'z_Exit',
|
||||
command: {
|
||||
id: 'workbench.action.quit',
|
||||
title: nls.localize({ key: 'miExit', comment: ['&& denotes a mnemonic'] }, "E&&xit")
|
||||
},
|
||||
order: 1,
|
||||
when: IsMacContext.toNegated()
|
||||
});
|
||||
|
||||
// Zoom
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, {
|
||||
group: '3_zoom',
|
||||
command: {
|
||||
id: ZoomInAction.ID,
|
||||
title: nls.localize({ key: 'miZoomIn', comment: ['&& denotes a mnemonic'] }, "&&Zoom In")
|
||||
},
|
||||
order: 1
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, {
|
||||
group: '3_zoom',
|
||||
command: {
|
||||
id: ZoomOutAction.ID,
|
||||
title: nls.localize({ key: 'miZoomOut', comment: ['&& denotes a mnemonic'] }, "&&Zoom Out")
|
||||
},
|
||||
order: 2
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, {
|
||||
group: '3_zoom',
|
||||
command: {
|
||||
id: ZoomResetAction.ID,
|
||||
title: nls.localize({ key: 'miZoomReset', comment: ['&& denotes a mnemonic'] }, "&&Reset Zoom")
|
||||
},
|
||||
order: 3
|
||||
});
|
||||
|
||||
if (!!product.reportIssueUrl) {
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, {
|
||||
group: '3_feedback',
|
||||
command: {
|
||||
id: 'workbench.action.openIssueReporter',
|
||||
title: nls.localize({ key: 'miReportIssue', comment: ['&& denotes a mnemonic', 'Translate this to "Report Issue in English" in all languages please!'] }, "Report &&Issue")
|
||||
},
|
||||
order: 3
|
||||
});
|
||||
}
|
||||
|
||||
// Tools
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, {
|
||||
group: '5_tools',
|
||||
command: {
|
||||
id: ToggleDevToolsAction.ID,
|
||||
title: nls.localize({ key: 'miToggleDevTools', comment: ['&& denotes a mnemonic'] }, "&&Toggle Developer Tools")
|
||||
},
|
||||
order: 1
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, {
|
||||
group: '5_tools',
|
||||
command: {
|
||||
id: 'workbench.action.openProcessExplorer',
|
||||
title: nls.localize({ key: 'miOpenProcessExplorerer', comment: ['&& denotes a mnemonic'] }, "Open &&Process Explorer")
|
||||
},
|
||||
order: 2
|
||||
});
|
||||
})();
|
||||
|
||||
// Configuration
|
||||
(function registerConfiguration(): void {
|
||||
const registry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration);
|
||||
|
||||
// Window
|
||||
registry.registerConfiguration({
|
||||
'id': 'window',
|
||||
'order': 8,
|
||||
'title': nls.localize('windowConfigurationTitle', "Window"),
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'window.openWithoutArgumentsInNewWindow': {
|
||||
'type': 'string',
|
||||
'enum': ['on', 'off'],
|
||||
'enumDescriptions': [
|
||||
nls.localize('window.openWithoutArgumentsInNewWindow.on', "Open a new empty window."),
|
||||
nls.localize('window.openWithoutArgumentsInNewWindow.off', "Focus the last active running instance.")
|
||||
],
|
||||
'default': isMacintosh ? 'off' : 'on',
|
||||
'scope': ConfigurationScope.APPLICATION,
|
||||
'markdownDescription': nls.localize('openWithoutArgumentsInNewWindow', "Controls whether a new empty window should open when starting a second instance without arguments or if the last running instance should get focus.\nNote that there can still be cases where this setting is ignored (e.g. when using the `--new-window` or `--reuse-window` command line option).")
|
||||
},
|
||||
'window.restoreWindows': {
|
||||
'type': 'string',
|
||||
'enum': ['all', 'folders', 'one', 'none'],
|
||||
'enumDescriptions': [
|
||||
nls.localize('window.reopenFolders.all', "Reopen all windows."),
|
||||
nls.localize('window.reopenFolders.folders', "Reopen all folders. Empty workspaces will not be restored."),
|
||||
nls.localize('window.reopenFolders.one', "Reopen the last active window."),
|
||||
nls.localize('window.reopenFolders.none', "Never reopen a window. Always start with an empty one.")
|
||||
],
|
||||
'default': 'all',
|
||||
'scope': ConfigurationScope.APPLICATION,
|
||||
'description': nls.localize('restoreWindows', "Controls how windows are being reopened after a restart.")
|
||||
},
|
||||
'window.restoreFullscreen': {
|
||||
'type': 'boolean',
|
||||
'default': false,
|
||||
'scope': ConfigurationScope.APPLICATION,
|
||||
'description': nls.localize('restoreFullscreen', "Controls whether a window should restore to full screen mode if it was exited in full screen mode.")
|
||||
},
|
||||
'window.zoomLevel': {
|
||||
'type': 'number',
|
||||
'default': 0,
|
||||
'description': nls.localize('zoomLevel', "Adjust the zoom level of the window. The original size is 0 and each increment above (e.g. 1) or below (e.g. -1) represents zooming 20% larger or smaller. You can also enter decimals to adjust the zoom level with a finer granularity.")
|
||||
},
|
||||
'window.newWindowDimensions': {
|
||||
'type': 'string',
|
||||
'enum': ['default', 'inherit', 'offset', 'maximized', 'fullscreen'],
|
||||
'enumDescriptions': [
|
||||
nls.localize('window.newWindowDimensions.default', "Open new windows in the center of the screen."),
|
||||
nls.localize('window.newWindowDimensions.inherit', "Open new windows with same dimension as last active one."),
|
||||
nls.localize('window.newWindowDimensions.offset', "Open new windows with same dimension as last active one with an offset position."),
|
||||
nls.localize('window.newWindowDimensions.maximized', "Open new windows maximized."),
|
||||
nls.localize('window.newWindowDimensions.fullscreen', "Open new windows in full screen mode.")
|
||||
],
|
||||
'default': 'default',
|
||||
'scope': ConfigurationScope.APPLICATION,
|
||||
'description': nls.localize('newWindowDimensions', "Controls the dimensions of opening a new window when at least one window is already opened. Note that this setting does not have an impact on the first window that is opened. The first window will always restore the size and location as you left it before closing.")
|
||||
},
|
||||
'window.closeWhenEmpty': {
|
||||
'type': 'boolean',
|
||||
'default': false,
|
||||
'description': nls.localize('closeWhenEmpty', "Controls whether closing the last editor should also close the window. This setting only applies for windows that do not show folders.")
|
||||
},
|
||||
'window.doubleClickIconToClose': {
|
||||
'type': 'boolean',
|
||||
'default': false,
|
||||
'scope': ConfigurationScope.APPLICATION,
|
||||
'markdownDescription': nls.localize('window.doubleClickIconToClose', "If enabled, double clicking the application icon in the title bar will close the window and the window cannot be dragged by the icon. This setting only has an effect when `#window.titleBarStyle#` is set to `custom`.")
|
||||
},
|
||||
'window.titleBarStyle': {
|
||||
'type': 'string',
|
||||
'enum': ['native', 'custom'],
|
||||
'default': isLinux ? 'native' : 'custom',
|
||||
'scope': ConfigurationScope.APPLICATION,
|
||||
'description': nls.localize('titleBarStyle', "Adjust the appearance of the window title bar. On Linux and Windows, this setting also affects the application and context menu appearances. Changes require a full restart to apply.")
|
||||
},
|
||||
'window.dialogStyle': {
|
||||
'type': 'string',
|
||||
'enum': ['native', 'custom'],
|
||||
'default': 'native',
|
||||
'scope': ConfigurationScope.APPLICATION,
|
||||
'description': nls.localize('dialogStyle', "Adjust the appearance of dialog windows.")
|
||||
},
|
||||
'window.nativeTabs': {
|
||||
'type': 'boolean',
|
||||
'default': false,
|
||||
'scope': ConfigurationScope.APPLICATION,
|
||||
'description': nls.localize('window.nativeTabs', "Enables macOS Sierra window tabs. Note that changes require a full restart to apply and that native tabs will disable a custom title bar style if configured."),
|
||||
'included': isMacintosh
|
||||
},
|
||||
'window.nativeFullScreen': {
|
||||
'type': 'boolean',
|
||||
'default': true,
|
||||
'description': nls.localize('window.nativeFullScreen', "Controls if native full-screen should be used on macOS. Disable this option to prevent macOS from creating a new space when going full-screen."),
|
||||
'scope': ConfigurationScope.APPLICATION,
|
||||
'included': isMacintosh
|
||||
},
|
||||
'window.clickThroughInactive': {
|
||||
'type': 'boolean',
|
||||
'default': true,
|
||||
'scope': ConfigurationScope.APPLICATION,
|
||||
'description': nls.localize('window.clickThroughInactive', "If enabled, clicking on an inactive window will both activate the window and trigger the element under the mouse if it is clickable. If disabled, clicking anywhere on an inactive window will activate it only and a second click is required on the element."),
|
||||
'included': isMacintosh
|
||||
},
|
||||
'window.enableExperimentalProxyLoginDialog': {
|
||||
'type': 'boolean',
|
||||
'default': false,
|
||||
'scope': ConfigurationScope.APPLICATION,
|
||||
'description': nls.localize('window.enableExperimentalProxyLoginDialog', "Enables a new login dialog for proxy authentication. Requires a restart to take effect."),
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Telemetry
|
||||
registry.registerConfiguration({
|
||||
'id': 'telemetry',
|
||||
'order': 110,
|
||||
title: nls.localize('telemetryConfigurationTitle', "Telemetry"),
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'telemetry.enableCrashReporter': {
|
||||
'type': 'boolean',
|
||||
'description': nls.localize('telemetry.enableCrashReporting', "Enable crash reports to be sent to a Microsoft online service. \nThis option requires restart to take effect."),
|
||||
'default': true,
|
||||
'tags': ['usesOnlineServices']
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Keybinding
|
||||
registry.registerConfiguration({
|
||||
'id': 'keyboard',
|
||||
'order': 15,
|
||||
'type': 'object',
|
||||
'title': nls.localize('keyboardConfigurationTitle', "Keyboard"),
|
||||
'properties': {
|
||||
'keyboard.touchbar.enabled': {
|
||||
'type': 'boolean',
|
||||
'default': true,
|
||||
'description': nls.localize('touchbar.enabled', "Enables the macOS touchbar buttons on the keyboard if available."),
|
||||
'included': isMacintosh
|
||||
},
|
||||
'keyboard.touchbar.ignored': {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': 'string'
|
||||
},
|
||||
'default': [],
|
||||
'markdownDescription': nls.localize('touchbar.ignored', 'A set of identifiers for entries in the touchbar that should not show up (for example `workbench.action.navigateBack`.'),
|
||||
'included': isMacintosh
|
||||
}
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
// JSON Schemas
|
||||
(function registerJSONSchemas(): void {
|
||||
const argvDefinitionFileSchemaId = 'vscode://schemas/argv';
|
||||
const jsonRegistry = Registry.as<IJSONContributionRegistry>(JSONExtensions.JSONContribution);
|
||||
const schema: IJSONSchema = {
|
||||
id: argvDefinitionFileSchemaId,
|
||||
allowComments: true,
|
||||
allowTrailingCommas: true,
|
||||
description: 'VSCode static command line definition file',
|
||||
type: 'object',
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
locale: {
|
||||
type: 'string',
|
||||
description: nls.localize('argv.locale', 'The display Language to use. Picking a different language requires the associated language pack to be installed.')
|
||||
},
|
||||
'disable-hardware-acceleration': {
|
||||
type: 'boolean',
|
||||
description: nls.localize('argv.disableHardwareAcceleration', 'Disables hardware acceleration. ONLY change this option if you encounter graphic issues.')
|
||||
},
|
||||
'disable-color-correct-rendering': {
|
||||
type: 'boolean',
|
||||
description: nls.localize('argv.disableColorCorrectRendering', 'Resolves issues around color profile selection. ONLY change this option if you encounter graphic issues.')
|
||||
},
|
||||
'force-color-profile': {
|
||||
type: 'string',
|
||||
markdownDescription: nls.localize('argv.forceColorProfile', 'Allows to override the color profile to use. If you experience colors appear badly, try to set this to `srgb` and restart.')
|
||||
},
|
||||
'enable-crash-reporter': {
|
||||
type: 'boolean',
|
||||
markdownDescription: nls.localize('argv.enableCrashReporter', 'Allows to disable crash reporting, should restart the app if the value is changed.')
|
||||
},
|
||||
'crash-reporter-id': {
|
||||
type: 'string',
|
||||
markdownDescription: nls.localize('argv.crashReporterId', 'Unique id used for correlating crash reports sent from this app instance.')
|
||||
},
|
||||
'enable-proposed-api': {
|
||||
type: 'array',
|
||||
description: nls.localize('argv.enebleProposedApi', "Enable proposed APIs for a list of extension ids (such as \`vscode.git\`). Proposed APIs are unstable and subject to breaking without warning at any time. This should only be set for extension development and testing purposes."),
|
||||
items: {
|
||||
type: 'string'
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
if (isLinux) {
|
||||
schema.properties!['force-renderer-accessibility'] = {
|
||||
type: 'boolean',
|
||||
description: nls.localize('argv.force-renderer-accessibility', 'Forces the renderer to be accessible. ONLY change this if you are using a screen reader on Linux. On other platforms the renderer will automatically be accessible. This flag is automatically set if you have editor.accessibilitySupport: on.'),
|
||||
};
|
||||
}
|
||||
|
||||
jsonRegistry.registerSchema(argvDefinitionFileSchemaId, schema);
|
||||
})();
|
||||
280
lib/vscode/src/vs/workbench/electron-sandbox/desktop.main.ts
Normal file
280
lib/vscode/src/vs/workbench/electron-sandbox/desktop.main.ts
Normal file
@@ -0,0 +1,280 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { zoomLevelToZoomFactor } from 'vs/platform/windows/common/windows';
|
||||
import { importEntries, mark } from 'vs/base/common/performance';
|
||||
import { Workbench } from 'vs/workbench/browser/workbench';
|
||||
import { NativeWindow } from 'vs/workbench/electron-sandbox/window';
|
||||
import { setZoomLevel, setZoomFactor, setFullscreen } from 'vs/base/browser/browser';
|
||||
import { domContentLoaded, addDisposableListener, EventType, scheduleAtNextAnimationFrame } from 'vs/base/browser/dom';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { reviveWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IMainProcessService, MainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService';
|
||||
import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver';
|
||||
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
|
||||
import { FileService } from 'vs/platform/files/common/fileService';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { RemoteFileSystemProvider } from 'vs/workbench/services/remote/common/remoteAgentFileSystemChannel';
|
||||
import { ISignService } from 'vs/platform/sign/common/sign';
|
||||
import { FileUserDataProvider } from 'vs/workbench/services/userData/common/fileUserDataProvider';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import product from 'vs/platform/product/common/product';
|
||||
import { INativeHostService } from 'vs/platform/native/electron-sandbox/native';
|
||||
import { NativeHostService } from 'vs/platform/native/electron-sandbox/nativeHostService';
|
||||
import { SimpleConfigurationService, simpleFileSystemProvider, SimpleLogService, SimpleRemoteAgentService, SimpleSignService, SimpleStorageService, SimpleNativeWorkbenchEnvironmentService, SimpleWorkspaceService } from 'vs/workbench/electron-sandbox/sandbox.simpleservices';
|
||||
import { INativeWorkbenchConfiguration, INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService';
|
||||
import { RemoteAuthorityResolverService } from 'vs/platform/remote/electron-sandbox/remoteAuthorityResolverService';
|
||||
|
||||
class DesktopMain extends Disposable {
|
||||
|
||||
private readonly environmentService = new SimpleNativeWorkbenchEnvironmentService(this.configuration);
|
||||
|
||||
constructor(private configuration: INativeWorkbenchConfiguration) {
|
||||
super();
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
private init(): void {
|
||||
|
||||
// Massage configuration file URIs
|
||||
this.reviveUris();
|
||||
|
||||
// Setup perf
|
||||
importEntries(this.configuration.perfEntries);
|
||||
|
||||
// Browser config
|
||||
const zoomLevel = this.configuration.zoomLevel || 0;
|
||||
setZoomFactor(zoomLevelToZoomFactor(zoomLevel));
|
||||
setZoomLevel(zoomLevel, true /* isTrusted */);
|
||||
setFullscreen(!!this.configuration.fullscreen);
|
||||
}
|
||||
|
||||
private reviveUris() {
|
||||
if (this.configuration.folderUri) {
|
||||
this.configuration.folderUri = URI.revive(this.configuration.folderUri);
|
||||
}
|
||||
|
||||
if (this.configuration.workspace) {
|
||||
this.configuration.workspace = reviveWorkspaceIdentifier(this.configuration.workspace);
|
||||
}
|
||||
|
||||
const filesToWait = this.configuration.filesToWait;
|
||||
const filesToWaitPaths = filesToWait?.paths;
|
||||
[filesToWaitPaths, this.configuration.filesToOpenOrCreate, this.configuration.filesToDiff].forEach(paths => {
|
||||
if (Array.isArray(paths)) {
|
||||
paths.forEach(path => {
|
||||
if (path.fileUri) {
|
||||
path.fileUri = URI.revive(path.fileUri);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (filesToWait) {
|
||||
filesToWait.waitMarkerFileUri = URI.revive(filesToWait.waitMarkerFileUri);
|
||||
}
|
||||
}
|
||||
|
||||
async open(): Promise<void> {
|
||||
const services = await this.initServices();
|
||||
|
||||
await domContentLoaded();
|
||||
mark('willStartWorkbench');
|
||||
|
||||
// Create Workbench
|
||||
const workbench = new Workbench(document.body, services.serviceCollection, services.logService);
|
||||
|
||||
// Listeners
|
||||
this.registerListeners(workbench, services.storageService);
|
||||
|
||||
// Startup
|
||||
const instantiationService = workbench.startup();
|
||||
|
||||
// Window
|
||||
this._register(instantiationService.createInstance(NativeWindow));
|
||||
|
||||
// Logging
|
||||
services.logService.trace('workbench configuration', JSON.stringify(this.configuration));
|
||||
}
|
||||
|
||||
private registerListeners(workbench: Workbench, storageService: SimpleStorageService): void {
|
||||
|
||||
// Layout
|
||||
this._register(addDisposableListener(window, EventType.RESIZE, e => this.onWindowResize(e, true, workbench)));
|
||||
|
||||
// Workbench Lifecycle
|
||||
this._register(workbench.onShutdown(() => this.dispose()));
|
||||
this._register(workbench.onWillShutdown(event => event.join(storageService.close())));
|
||||
}
|
||||
|
||||
private onWindowResize(e: Event, retry: boolean, workbench: Workbench): void {
|
||||
if (e.target === window) {
|
||||
if (window.document && window.document.body && window.document.body.clientWidth === 0) {
|
||||
// TODO@Ben this is an electron issue on macOS when simple fullscreen is enabled
|
||||
// where for some reason the window clientWidth is reported as 0 when switching
|
||||
// between simple fullscreen and normal screen. In that case we schedule the layout
|
||||
// call at the next animation frame once, in the hope that the dimensions are
|
||||
// proper then.
|
||||
if (retry) {
|
||||
scheduleAtNextAnimationFrame(() => this.onWindowResize(e, false, workbench));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
workbench.layout();
|
||||
}
|
||||
}
|
||||
|
||||
private async initServices(): Promise<{ serviceCollection: ServiceCollection, logService: ILogService, storageService: SimpleStorageService }> {
|
||||
const serviceCollection = new ServiceCollection();
|
||||
|
||||
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
//
|
||||
// NOTE: Please do NOT register services here. Use `registerSingleton()`
|
||||
// from `workbench.common.main.ts` if the service is shared between
|
||||
// desktop and web or `workbench.sandbox.main.ts` if the service
|
||||
// is desktop only.
|
||||
//
|
||||
// DO NOT add services to `workbench.desktop.main.ts`, always add
|
||||
// to `workbench.sandbox.main.ts` to support our Electron sandbox
|
||||
//
|
||||
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
|
||||
// Main Process
|
||||
const mainProcessService = this._register(new MainProcessService(this.configuration.windowId));
|
||||
serviceCollection.set(IMainProcessService, mainProcessService);
|
||||
|
||||
// Environment
|
||||
serviceCollection.set(IWorkbenchEnvironmentService, this.environmentService);
|
||||
serviceCollection.set(INativeWorkbenchEnvironmentService, this.environmentService);
|
||||
|
||||
// Product
|
||||
const productService: IProductService = { _serviceBrand: undefined, ...product };
|
||||
serviceCollection.set(IProductService, productService);
|
||||
|
||||
// Log
|
||||
const logService = new SimpleLogService();
|
||||
serviceCollection.set(ILogService, logService);
|
||||
|
||||
// Remote
|
||||
const remoteAuthorityResolverService = new RemoteAuthorityResolverService();
|
||||
serviceCollection.set(IRemoteAuthorityResolverService, remoteAuthorityResolverService);
|
||||
|
||||
// Sign
|
||||
const signService = new SimpleSignService();
|
||||
serviceCollection.set(ISignService, signService);
|
||||
|
||||
// Remote Agent
|
||||
const remoteAgentService = new SimpleRemoteAgentService();
|
||||
serviceCollection.set(IRemoteAgentService, remoteAgentService);
|
||||
|
||||
|
||||
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
//
|
||||
// NOTE: Please do NOT register services here. Use `registerSingleton()`
|
||||
// from `workbench.common.main.ts` if the service is shared between
|
||||
// desktop and web or `workbench.sandbox.main.ts` if the service
|
||||
// is desktop only.
|
||||
//
|
||||
// DO NOT add services to `workbench.desktop.main.ts`, always add
|
||||
// to `workbench.sandbox.main.ts` to support our Electron sandbox
|
||||
//
|
||||
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
|
||||
// Native Host
|
||||
const nativeHostService = new NativeHostService(this.configuration.windowId, mainProcessService) as INativeHostService;
|
||||
serviceCollection.set(INativeHostService, nativeHostService);
|
||||
|
||||
// Files
|
||||
const fileService = this._register(new FileService(logService));
|
||||
serviceCollection.set(IFileService, fileService);
|
||||
|
||||
fileService.registerProvider(Schemas.file, simpleFileSystemProvider);
|
||||
|
||||
// User Data Provider
|
||||
fileService.registerProvider(Schemas.userData, new FileUserDataProvider(URI.file('user-home'), undefined, simpleFileSystemProvider, this.environmentService, logService));
|
||||
|
||||
const connection = remoteAgentService.getConnection();
|
||||
if (connection) {
|
||||
const remoteFileSystemProvider = this._register(new RemoteFileSystemProvider(remoteAgentService));
|
||||
fileService.registerProvider(Schemas.vscodeRemote, remoteFileSystemProvider);
|
||||
}
|
||||
|
||||
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
//
|
||||
// NOTE: Please do NOT register services here. Use `registerSingleton()`
|
||||
// from `workbench.common.main.ts` if the service is shared between
|
||||
// desktop and web or `workbench.sandbox.main.ts` if the service
|
||||
// is desktop only.
|
||||
//
|
||||
// DO NOT add services to `workbench.desktop.main.ts`, always add
|
||||
// to `workbench.sandbox.main.ts` to support our Electron sandbox
|
||||
//
|
||||
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
|
||||
const services = await Promise.all([
|
||||
this.createWorkspaceService().then(service => {
|
||||
|
||||
// Workspace
|
||||
serviceCollection.set(IWorkspaceContextService, service);
|
||||
|
||||
// Configuration
|
||||
serviceCollection.set(IConfigurationService, new SimpleConfigurationService());
|
||||
|
||||
return service;
|
||||
}),
|
||||
|
||||
this.createStorageService().then(service => {
|
||||
|
||||
// Storage
|
||||
serviceCollection.set(IStorageService, service);
|
||||
|
||||
return service;
|
||||
})
|
||||
]);
|
||||
|
||||
|
||||
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
//
|
||||
// NOTE: Please do NOT register services here. Use `registerSingleton()`
|
||||
// from `workbench.common.main.ts` if the service is shared between
|
||||
// desktop and web or `workbench.sandbox.main.ts` if the service
|
||||
// is desktop only.
|
||||
//
|
||||
// DO NOT add services to `workbench.desktop.main.ts`, always add
|
||||
// to `workbench.sandbox.main.ts` to support our Electron sandbox
|
||||
//
|
||||
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
|
||||
return { serviceCollection, logService, storageService: services[1] };
|
||||
}
|
||||
|
||||
private async createWorkspaceService(): Promise<IWorkspaceContextService> {
|
||||
return new SimpleWorkspaceService();
|
||||
}
|
||||
|
||||
private async createStorageService(): Promise<SimpleStorageService> {
|
||||
return new SimpleStorageService();
|
||||
}
|
||||
}
|
||||
|
||||
export function main(configuration: INativeWorkbenchConfiguration): Promise<void> {
|
||||
const workbench = new DesktopMain(configuration);
|
||||
|
||||
return workbench.open();
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { getZoomFactor } from 'vs/base/browser/browser';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { IHostService } from 'vs/workbench/services/host/browser/host';
|
||||
import { isMacintosh, isWindows, isLinux } from 'vs/base/common/platform';
|
||||
import { IMenuService } from 'vs/platform/actions/common/actions';
|
||||
import { TitlebarPart as BrowserTitleBarPart } from 'vs/workbench/browser/parts/titlebar/titlebarPart';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { INativeHostService } from 'vs/platform/native/electron-sandbox/native';
|
||||
import { getTitleBarStyle } from 'vs/platform/windows/common/windows';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { Codicon } from 'vs/base/common/codicons';
|
||||
|
||||
export class TitlebarPart extends BrowserTitleBarPart {
|
||||
private appIcon: HTMLElement | undefined;
|
||||
private windowControls: HTMLElement | undefined;
|
||||
private maxRestoreControl: HTMLElement | undefined;
|
||||
private dragRegion: HTMLElement | undefined;
|
||||
private resizer: HTMLElement | undefined;
|
||||
|
||||
constructor(
|
||||
@IContextMenuService contextMenuService: IContextMenuService,
|
||||
@IConfigurationService protected readonly configurationService: IConfigurationService,
|
||||
@IEditorService editorService: IEditorService,
|
||||
@IWorkbenchEnvironmentService protected readonly environmentService: IWorkbenchEnvironmentService,
|
||||
@IWorkspaceContextService contextService: IWorkspaceContextService,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@ILabelService labelService: ILabelService,
|
||||
@IStorageService storageService: IStorageService,
|
||||
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
|
||||
@IMenuService menuService: IMenuService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IHostService hostService: IHostService,
|
||||
@IProductService productService: IProductService,
|
||||
@INativeHostService private readonly nativeHostService: INativeHostService
|
||||
) {
|
||||
super(contextMenuService, configurationService, editorService, environmentService, contextService, instantiationService, themeService, labelService, storageService, layoutService, menuService, contextKeyService, hostService, productService);
|
||||
}
|
||||
|
||||
private onUpdateAppIconDragBehavior() {
|
||||
const setting = this.configurationService.getValue('window.doubleClickIconToClose');
|
||||
if (setting && this.appIcon) {
|
||||
(this.appIcon.style as any)['-webkit-app-region'] = 'no-drag';
|
||||
} else if (this.appIcon) {
|
||||
(this.appIcon.style as any)['-webkit-app-region'] = 'drag';
|
||||
}
|
||||
}
|
||||
|
||||
private onDidChangeMaximized(maximized: boolean) {
|
||||
if (this.maxRestoreControl) {
|
||||
if (maximized) {
|
||||
this.maxRestoreControl.classList.remove(...Codicon.chromeMaximize.classNamesArray);
|
||||
this.maxRestoreControl.classList.add(...Codicon.chromeRestore.classNamesArray);
|
||||
} else {
|
||||
this.maxRestoreControl.classList.remove(...Codicon.chromeRestore.classNamesArray);
|
||||
this.maxRestoreControl.classList.add(...Codicon.chromeMaximize.classNamesArray);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.resizer) {
|
||||
if (maximized) {
|
||||
DOM.hide(this.resizer);
|
||||
} else {
|
||||
DOM.show(this.resizer);
|
||||
}
|
||||
}
|
||||
|
||||
this.adjustTitleMarginToCenter();
|
||||
}
|
||||
|
||||
private onMenubarFocusChanged(focused: boolean) {
|
||||
if ((isWindows || isLinux) && this.currentMenubarVisibility !== 'compact' && this.dragRegion) {
|
||||
if (focused) {
|
||||
DOM.hide(this.dragRegion);
|
||||
} else {
|
||||
DOM.show(this.dragRegion);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected onMenubarVisibilityChanged(visible: boolean) {
|
||||
// Hide title when toggling menu bar
|
||||
if ((isWindows || isLinux) && this.currentMenubarVisibility === 'toggle' && visible) {
|
||||
// Hack to fix issue #52522 with layered webkit-app-region elements appearing under cursor
|
||||
if (this.dragRegion) {
|
||||
DOM.hide(this.dragRegion);
|
||||
setTimeout(() => DOM.show(this.dragRegion!), 50);
|
||||
}
|
||||
}
|
||||
|
||||
super.onMenubarVisibilityChanged(visible);
|
||||
}
|
||||
|
||||
protected onConfigurationChanged(event: IConfigurationChangeEvent): void {
|
||||
|
||||
super.onConfigurationChanged(event);
|
||||
|
||||
if (event.affectsConfiguration('window.doubleClickIconToClose')) {
|
||||
if (this.appIcon) {
|
||||
this.onUpdateAppIconDragBehavior();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected adjustTitleMarginToCenter(): void {
|
||||
if (this.customMenubar && this.menubar) {
|
||||
const leftMarker = (this.appIcon ? this.appIcon.clientWidth : 0) + this.menubar.clientWidth + 10;
|
||||
const rightMarker = this.element.clientWidth - (this.windowControls ? this.windowControls.clientWidth : 0) - 10;
|
||||
|
||||
// Not enough space to center the titlebar within window,
|
||||
// Center between menu and window controls
|
||||
if (leftMarker > (this.element.clientWidth - this.title.clientWidth) / 2 ||
|
||||
rightMarker < (this.element.clientWidth + this.title.clientWidth) / 2) {
|
||||
this.title.style.position = '';
|
||||
this.title.style.left = '';
|
||||
this.title.style.transform = '';
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.title.style.position = 'absolute';
|
||||
this.title.style.left = '50%';
|
||||
this.title.style.transform = 'translate(-50%, 0)';
|
||||
}
|
||||
|
||||
protected installMenubar(): void {
|
||||
super.installMenubar();
|
||||
|
||||
if (this.menubar) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.customMenubar) {
|
||||
this._register(this.customMenubar.onFocusStateChange(e => this.onMenubarFocusChanged(e)));
|
||||
}
|
||||
}
|
||||
|
||||
createContentArea(parent: HTMLElement): HTMLElement {
|
||||
const ret = super.createContentArea(parent);
|
||||
|
||||
// App Icon (Native Windows/Linux)
|
||||
if (!isMacintosh) {
|
||||
this.appIcon = DOM.prepend(this.element, DOM.$('div.window-appicon'));
|
||||
this.onUpdateAppIconDragBehavior();
|
||||
|
||||
this._register(DOM.addDisposableListener(this.appIcon, DOM.EventType.DBLCLICK, (e => {
|
||||
this.nativeHostService.closeWindow();
|
||||
})));
|
||||
}
|
||||
|
||||
// Draggable region that we can manipulate for #52522
|
||||
this.dragRegion = DOM.prepend(this.element, DOM.$('div.titlebar-drag-region'));
|
||||
|
||||
// Window Controls (Native Windows/Linux)
|
||||
if (!isMacintosh) {
|
||||
this.windowControls = DOM.append(this.element, DOM.$('div.window-controls-container'));
|
||||
|
||||
// Minimize
|
||||
const minimizeIcon = DOM.append(this.windowControls, DOM.$('div.window-icon.window-minimize' + Codicon.chromeMinimize.cssSelector));
|
||||
this._register(DOM.addDisposableListener(minimizeIcon, DOM.EventType.CLICK, e => {
|
||||
this.nativeHostService.minimizeWindow();
|
||||
}));
|
||||
|
||||
// Restore
|
||||
this.maxRestoreControl = DOM.append(this.windowControls, DOM.$('div.window-icon.window-max-restore'));
|
||||
this._register(DOM.addDisposableListener(this.maxRestoreControl, DOM.EventType.CLICK, async e => {
|
||||
const maximized = await this.nativeHostService.isMaximized();
|
||||
if (maximized) {
|
||||
return this.nativeHostService.unmaximizeWindow();
|
||||
}
|
||||
|
||||
return this.nativeHostService.maximizeWindow();
|
||||
}));
|
||||
|
||||
// Close
|
||||
const closeIcon = DOM.append(this.windowControls, DOM.$('div.window-icon.window-close' + Codicon.chromeClose.cssSelector));
|
||||
this._register(DOM.addDisposableListener(closeIcon, DOM.EventType.CLICK, e => {
|
||||
this.nativeHostService.closeWindow();
|
||||
}));
|
||||
|
||||
// Resizer
|
||||
this.resizer = DOM.append(this.element, DOM.$('div.resizer'));
|
||||
|
||||
this._register(this.layoutService.onMaximizeChange(maximized => this.onDidChangeMaximized(maximized)));
|
||||
this.onDidChangeMaximized(this.layoutService.isWindowMaximized());
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
updateLayout(dimension: DOM.Dimension): void {
|
||||
this.lastLayoutDimensions = dimension;
|
||||
|
||||
if (getTitleBarStyle(this.configurationService, this.environmentService) === 'custom') {
|
||||
// Only prevent zooming behavior on macOS or when the menubar is not visible
|
||||
if (isMacintosh || this.currentMenubarVisibility === 'hidden') {
|
||||
this.title.style.zoom = `${1 / getZoomFactor()}`;
|
||||
if (isWindows || isLinux) {
|
||||
if (this.appIcon) {
|
||||
this.appIcon.style.zoom = `${1 / getZoomFactor()}`;
|
||||
}
|
||||
|
||||
if (this.windowControls) {
|
||||
this.windowControls.style.zoom = `${1 / getZoomFactor()}`;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.title.style.zoom = '';
|
||||
if (isWindows || isLinux) {
|
||||
if (this.appIcon) {
|
||||
this.appIcon.style.zoom = '';
|
||||
}
|
||||
|
||||
if (this.windowControls) {
|
||||
this.windowControls.style.zoom = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DOM.runAtThisOrScheduleAtNextAnimationFrame(() => this.adjustTitleMarginToCenter());
|
||||
|
||||
if (this.customMenubar) {
|
||||
const menubarDimension = new DOM.Dimension(0, dimension.height);
|
||||
this.customMenubar.layout(menubarDimension);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,834 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/* eslint-disable code-no-standalone-editor */
|
||||
/* eslint-disable code-import-patterns */
|
||||
|
||||
import { ConsoleLogService } from 'vs/platform/log/common/log';
|
||||
import { ISignService } from 'vs/platform/sign/common/sign';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IRemoteAgentConnection, IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
|
||||
import { IDiagnosticInfoOptions, IDiagnosticInfo } from 'vs/platform/diagnostics/common/diagnostics';
|
||||
import { IAddressProvider, ISocketFactory } from 'vs/platform/remote/common/remoteAgentConnection';
|
||||
import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment';
|
||||
import { ITelemetryData, ITelemetryInfo, ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { BrowserSocketFactory } from 'vs/platform/remote/browser/browserSocketFactory';
|
||||
import { ExtensionIdentifier, IExtension, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { SimpleConfigurationService as BaseSimpleConfigurationService } from 'vs/editor/standalone/browser/simpleServices';
|
||||
import { InMemoryStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { IBackupFileService, IResolvedBackup } from 'vs/workbench/services/backup/common/backup';
|
||||
import { ITextSnapshot } from 'vs/editor/common/model';
|
||||
import { IExtensionService, NullExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { ClassifiedEvent, GDPRClassification, StrictPropertyChecker } from 'vs/platform/telemetry/common/gdprTypings';
|
||||
import { IKeyboardLayoutInfo, IKeymapService, ILinuxKeyboardLayoutInfo, ILinuxKeyboardMapping, IMacKeyboardLayoutInfo, IMacKeyboardMapping, IWindowsKeyboardLayoutInfo, IWindowsKeyboardMapping } from 'vs/workbench/services/keybinding/common/keymapInfo';
|
||||
import { IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { DispatchConfig } from 'vs/workbench/services/keybinding/common/dispatchConfig';
|
||||
import { IKeyboardMapper } from 'vs/workbench/services/keybinding/common/keyboardMapper';
|
||||
import { ChordKeybinding, ResolvedKeybinding, SimpleKeybinding } from 'vs/base/common/keyCodes';
|
||||
import { ScanCodeBinding } from 'vs/base/common/scanCode';
|
||||
import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding';
|
||||
import { isWindows, OS } from 'vs/base/common/platform';
|
||||
import { IWebviewService, WebviewContentOptions, WebviewElement, WebviewExtensionDescription, WebviewIcons, WebviewOptions, WebviewOverlay } from 'vs/workbench/contrib/webview/browser/webview';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { AbstractTextFileService } from 'vs/workbench/services/textfile/browser/textFileService';
|
||||
import { IExtensionManagementServer, IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
|
||||
import { ITunnelProvider, ITunnelService, RemoteTunnel } from 'vs/platform/remote/common/tunnel';
|
||||
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IManualSyncTask, IResourcePreview, ISyncResourceHandle, ISyncTask, IUserDataAutoSyncService, IUserDataSyncService, IUserDataSyncStore, IUserDataSyncStoreManagementService, SyncResource, SyncStatus, UserDataSyncStoreType } from 'vs/platform/userDataSync/common/userDataSync';
|
||||
import { IUserDataSyncAccount, IUserDataSyncAccountService } from 'vs/platform/userDataSync/common/userDataSyncAccount';
|
||||
import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { ITaskProvider, ITaskService, ITaskSummary, ProblemMatcherRunOptions, Task, TaskFilter, TaskTerminateResponse, WorkspaceFolderTaskResult } from 'vs/workbench/contrib/tasks/common/taskService';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { LinkedMap } from 'vs/base/common/map';
|
||||
import { IWorkspace, IWorkspaceContextService, IWorkspaceFolder, WorkbenchState, WorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { CustomTask, ContributedTask, InMemoryTask, TaskRunSource, ConfiguringTask, TaskIdentifier, TaskSorter } from 'vs/workbench/contrib/tasks/common/tasks';
|
||||
import { TaskSystemInfo } from 'vs/workbench/contrib/tasks/common/taskSystem';
|
||||
import { IExtensionTipsService, IConfigBasedExtensionTip, IExecutableBasedExtensionTip, IWorkspaceTips } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { IWorkspaceTagsService, Tags } from 'vs/workbench/contrib/tags/common/workspaceTags';
|
||||
import { AsbtractOutputChannelModelService, IOutputChannelModelService } from 'vs/workbench/services/output/common/outputChannelModel';
|
||||
import { joinPath } from 'vs/base/common/resources';
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { IIntegrityService, IntegrityTestResult } from 'vs/workbench/services/integrity/common/integrity';
|
||||
import { INativeWorkbenchConfiguration, INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService';
|
||||
import { NativeParsedArgs } from 'vs/platform/environment/common/argv';
|
||||
import { IExtensionHostDebugParams } from 'vs/platform/environment/common/environment';
|
||||
import type { IWorkbenchConstructionOptions } from 'vs/workbench/workbench.web.api';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
|
||||
|
||||
|
||||
//#region Environment
|
||||
|
||||
export class SimpleNativeWorkbenchEnvironmentService implements INativeWorkbenchEnvironmentService {
|
||||
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
constructor(
|
||||
readonly configuration: INativeWorkbenchConfiguration
|
||||
) { }
|
||||
|
||||
get userRoamingDataHome(): URI { return URI.file('/sandbox-user-data-dir').with({ scheme: Schemas.userData }); }
|
||||
get settingsResource(): URI { return joinPath(this.userRoamingDataHome, 'settings.json'); }
|
||||
get argvResource(): URI { return joinPath(this.userRoamingDataHome, 'argv.json'); }
|
||||
get snippetsHome(): URI { return joinPath(this.userRoamingDataHome, 'snippets'); }
|
||||
get globalStorageHome(): URI { return URI.joinPath(this.userRoamingDataHome, 'globalStorage'); }
|
||||
get workspaceStorageHome(): URI { return URI.joinPath(this.userRoamingDataHome, 'workspaceStorage'); }
|
||||
get keybindingsResource(): URI { return joinPath(this.userRoamingDataHome, 'keybindings.json'); }
|
||||
get logFile(): URI { return joinPath(this.userRoamingDataHome, 'window.log'); }
|
||||
get untitledWorkspacesHome(): URI { return joinPath(this.userRoamingDataHome, 'Workspaces'); }
|
||||
get serviceMachineIdResource(): URI { return joinPath(this.userRoamingDataHome, 'machineid'); }
|
||||
get userDataSyncLogResource(): URI { return joinPath(this.userRoamingDataHome, 'syncLog'); }
|
||||
get userDataSyncHome(): URI { return joinPath(this.userRoamingDataHome, 'syncHome'); }
|
||||
get tmpDir(): URI { return joinPath(this.userRoamingDataHome, 'tmp'); }
|
||||
get logsPath(): string { return joinPath(this.userRoamingDataHome, 'logs').path; }
|
||||
|
||||
get backupWorkspaceHome(): URI { return joinPath(this.userRoamingDataHome, 'Backups', 'workspace'); }
|
||||
updateBackupPath(newPath: string | undefined): void { }
|
||||
|
||||
sessionId = this.configuration.sessionId;
|
||||
machineId = this.configuration.machineId;
|
||||
remoteAuthority = this.configuration.remoteAuthority;
|
||||
|
||||
options?: IWorkbenchConstructionOptions | undefined;
|
||||
logExtensionHostCommunication?: boolean | undefined;
|
||||
extensionEnabledProposedApi?: string[] | undefined;
|
||||
webviewExternalEndpoint: string = undefined!;
|
||||
webviewResourceRoot: string = undefined!;
|
||||
webviewCspSource: string = undefined!;
|
||||
skipReleaseNotes: boolean = undefined!;
|
||||
keyboardLayoutResource: URI = undefined!;
|
||||
sync: 'on' | 'off' | undefined;
|
||||
debugExtensionHost: IExtensionHostDebugParams = undefined!;
|
||||
debugRenderer = false;
|
||||
isExtensionDevelopment: boolean = false;
|
||||
disableExtensions: boolean | string[] = [];
|
||||
extensionDevelopmentLocationURI?: URI[] | undefined;
|
||||
extensionTestsLocationURI?: URI | undefined;
|
||||
logLevel?: string | undefined;
|
||||
|
||||
args: NativeParsedArgs = Object.create(null);
|
||||
|
||||
execPath: string = undefined!;
|
||||
appRoot: string = undefined!;
|
||||
userHome: URI = undefined!;
|
||||
appSettingsHome: URI = undefined!;
|
||||
userDataPath: string = undefined!;
|
||||
machineSettingsResource: URI = undefined!;
|
||||
|
||||
log?: string | undefined;
|
||||
extHostLogsPath: URI = undefined!;
|
||||
|
||||
installSourcePath: string = undefined!;
|
||||
|
||||
sharedIPCHandle: string = undefined!;
|
||||
|
||||
extensionsPath?: string | undefined;
|
||||
extensionsDownloadPath: string = undefined!;
|
||||
builtinExtensionsPath: string = undefined!;
|
||||
|
||||
driverHandle?: string | undefined;
|
||||
|
||||
crashReporterDirectory?: string | undefined;
|
||||
crashReporterId?: string | undefined;
|
||||
|
||||
nodeCachedDataDir?: string | undefined;
|
||||
|
||||
verbose = false;
|
||||
isBuilt = false;
|
||||
|
||||
get telemetryLogResource(): URI { return joinPath(this.userRoamingDataHome, 'telemetry.log'); }
|
||||
disableTelemetry = false;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
|
||||
//#region Workspace
|
||||
|
||||
export const workspaceResource = URI.file(isWindows ? '\\simpleWorkspace' : '/simpleWorkspace');
|
||||
|
||||
export class SimpleWorkspaceService implements IWorkspaceContextService {
|
||||
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
readonly onDidChangeWorkspaceName = Event.None;
|
||||
readonly onDidChangeWorkspaceFolders = Event.None;
|
||||
readonly onDidChangeWorkbenchState = Event.None;
|
||||
|
||||
private readonly workspace: IWorkspace;
|
||||
|
||||
constructor() {
|
||||
this.workspace = { id: '4064f6ec-cb38-4ad0-af64-ee6467e63c82', folders: [new WorkspaceFolder({ uri: workspaceResource, name: '', index: 0 })] };
|
||||
}
|
||||
|
||||
async getCompleteWorkspace(): Promise<IWorkspace> { return this.getWorkspace(); }
|
||||
|
||||
getWorkspace(): IWorkspace { return this.workspace; }
|
||||
|
||||
getWorkbenchState(): WorkbenchState {
|
||||
if (this.workspace) {
|
||||
if (this.workspace.configuration) {
|
||||
return WorkbenchState.WORKSPACE;
|
||||
}
|
||||
return WorkbenchState.FOLDER;
|
||||
}
|
||||
return WorkbenchState.EMPTY;
|
||||
}
|
||||
|
||||
getWorkspaceFolder(resource: URI): IWorkspaceFolder | null { return resource && resource.scheme === workspaceResource.scheme ? this.workspace.folders[0] : null; }
|
||||
isInsideWorkspace(resource: URI): boolean { return resource && resource.scheme === workspaceResource.scheme; }
|
||||
isCurrentWorkspace(workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): boolean { return true; }
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
|
||||
//#region Configuration
|
||||
|
||||
export class SimpleStorageService extends InMemoryStorageService { }
|
||||
|
||||
//#endregion
|
||||
|
||||
|
||||
//#region Configuration
|
||||
|
||||
export class SimpleConfigurationService extends BaseSimpleConfigurationService { }
|
||||
|
||||
//#endregion
|
||||
|
||||
|
||||
//#region Logger
|
||||
|
||||
export class SimpleLogService extends ConsoleLogService { }
|
||||
|
||||
export class SimpleSignService implements ISignService {
|
||||
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
async sign(value: string): Promise<string> { return value; }
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
|
||||
//#region Files
|
||||
|
||||
class SimpleFileSystemProvider extends InMemoryFileSystemProvider { }
|
||||
|
||||
export const simpleFileSystemProvider = new SimpleFileSystemProvider();
|
||||
|
||||
function createFile(parent: string, name: string, content: string = ''): void {
|
||||
simpleFileSystemProvider.writeFile(joinPath(workspaceResource, parent, name), VSBuffer.fromString(content).buffer, { create: true, overwrite: true });
|
||||
}
|
||||
|
||||
function createFolder(name: string): void {
|
||||
simpleFileSystemProvider.mkdir(joinPath(workspaceResource, name));
|
||||
}
|
||||
|
||||
createFolder('');
|
||||
createFolder('src');
|
||||
createFolder('test');
|
||||
|
||||
createFile('', '.gitignore', `out
|
||||
node_modules
|
||||
.vscode-test/
|
||||
*.vsix
|
||||
`);
|
||||
|
||||
createFile('', '.vscodeignore', `.vscode/**
|
||||
.vscode-test/**
|
||||
out/test/**
|
||||
src/**
|
||||
.gitignore
|
||||
vsc-extension-quickstart.md
|
||||
**/tsconfig.json
|
||||
**/tslint.json
|
||||
**/*.map
|
||||
**/*.ts`);
|
||||
|
||||
createFile('', 'CHANGELOG.md', `# Change Log
|
||||
All notable changes to the "test-ts" extension will be documented in this file.
|
||||
|
||||
Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file.
|
||||
|
||||
## [Unreleased]
|
||||
- Initial release`);
|
||||
createFile('', 'package.json', `{
|
||||
"name": "test-ts",
|
||||
"displayName": "test-ts",
|
||||
"description": "",
|
||||
"version": "0.0.1",
|
||||
"engines": {
|
||||
"vscode": "^1.31.0"
|
||||
},
|
||||
"categories": [
|
||||
"Other"
|
||||
],
|
||||
"activationEvents": [
|
||||
"onCommand:extension.helloWorld"
|
||||
],
|
||||
"main": "./out/extension.js",
|
||||
"contributes": {
|
||||
"commands": [
|
||||
{
|
||||
"command": "extension.helloWorld",
|
||||
"title": "Hello World"
|
||||
}
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"vscode:prepublish": "npm run compile",
|
||||
"compile": "tsc -p ./",
|
||||
"watch": "tsc -watch -p ./",
|
||||
"postinstall": "node ./node_modules/vscode/bin/install",
|
||||
"test": "npm run compile && node ./node_modules/vscode/bin/test"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^3.3.1",
|
||||
"vscode": "^1.1.28",
|
||||
"tslint": "^5.12.1",
|
||||
"@types/node": "^8.10.25",
|
||||
"@types/mocha": "^2.2.42"
|
||||
}
|
||||
}
|
||||
`);
|
||||
|
||||
createFile('', 'tsconfig.json', `{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es6",
|
||||
"outDir": "out",
|
||||
"lib": [
|
||||
"es6"
|
||||
],
|
||||
"sourceMap": true,
|
||||
"rootDir": "src",
|
||||
"strict": true /* enable all strict type-checking options */
|
||||
/* Additional Checks */
|
||||
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
||||
// "noUnusedParameters": true, /* Report errors on unused parameters. */
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
".vscode-test"
|
||||
]
|
||||
}
|
||||
`);
|
||||
|
||||
createFile('', 'tslint.json', `{
|
||||
"rules": {
|
||||
"no-string-throw": true,
|
||||
"no-unused-expression": true,
|
||||
"no-duplicate-variable": true,
|
||||
"curly": true,
|
||||
"class-name": true,
|
||||
"semicolon": [
|
||||
true,
|
||||
"always"
|
||||
],
|
||||
"triple-equals": true
|
||||
},
|
||||
"defaultSeverity": "warning"
|
||||
}
|
||||
`);
|
||||
|
||||
createFile('src', 'extension.ts', `// The module 'vscode' contains the VS Code extensibility API
|
||||
// Import the module and reference it with the alias vscode in your code below
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
// this method is called when your extension is activated
|
||||
// your extension is activated the very first time the command is executed
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
|
||||
// Use the console to output diagnostic information (console.log) and errors (console.error)
|
||||
// This line of code will only be executed once when your extension is activated
|
||||
console.log('Congratulations, your extension "test-ts" is now active!');
|
||||
|
||||
// The command has been defined in the package.json file
|
||||
// Now provide the implementation of the command with registerCommand
|
||||
// The commandId parameter must match the command field in package.json
|
||||
let disposable = vscode.commands.registerCommand('extension.helloWorld', () => {
|
||||
// The code you place here will be executed every time your command is executed
|
||||
|
||||
// Display a message box to the user
|
||||
vscode.window.showInformationMessage('Hello World!');
|
||||
});
|
||||
|
||||
context.subscriptions.push(disposable);
|
||||
}
|
||||
|
||||
// this method is called when your extension is deactivated
|
||||
export function deactivate() {}
|
||||
`);
|
||||
|
||||
createFile('test', 'extension.test.ts', `//
|
||||
// Note: This example test is leveraging the Mocha test framework.
|
||||
// Please refer to their documentation on https://mochajs.org/ for help.
|
||||
//
|
||||
|
||||
// The module 'assert' provides assertion methods from node
|
||||
import * as assert from 'assert';
|
||||
|
||||
// You can import and use all API from the 'vscode' module
|
||||
// as well as import your extension to test it
|
||||
// import * as vscode from 'vscode';
|
||||
// import * as myExtension from '../extension';
|
||||
|
||||
// Defines a Mocha test suite to group tests of similar kind together
|
||||
suite("Extension Tests", function () {
|
||||
|
||||
// Defines a Mocha unit test
|
||||
test("Something 1", function() {
|
||||
assert.equal(-1, [1, 2, 3].indexOf(5));
|
||||
assert.equal(-1, [1, 2, 3].indexOf(0));
|
||||
});
|
||||
});`);
|
||||
|
||||
createFile('test', 'index.ts', `//
|
||||
// PLEASE DO NOT MODIFY / DELETE UNLESS YOU KNOW WHAT YOU ARE DOING
|
||||
//
|
||||
// This file is providing the test runner to use when running extension tests.
|
||||
// By default the test runner in use is Mocha based.
|
||||
//
|
||||
// You can provide your own test runner if you want to override it by exporting
|
||||
// a function run(testRoot: string, clb: (error:Error) => void) that the extension
|
||||
// host can call to run the tests. The test runner is expected to use console.log
|
||||
// to report the results back to the caller. When the tests are finished, return
|
||||
// a possible error to the callback or null if none.
|
||||
|
||||
import * as testRunner from 'vscode/lib/testrunner';
|
||||
|
||||
// You can directly control Mocha options by configuring the test runner below
|
||||
// See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options
|
||||
// for more info
|
||||
testRunner.configure({
|
||||
ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.)
|
||||
useColors: true // colored output from test results
|
||||
});
|
||||
|
||||
module.exports = testRunner;`);
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Remote
|
||||
|
||||
export class SimpleRemoteAgentService implements IRemoteAgentService {
|
||||
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
socketFactory: ISocketFactory = new BrowserSocketFactory(null);
|
||||
|
||||
getConnection(): IRemoteAgentConnection | null { return null; }
|
||||
async getEnvironment(bail?: boolean): Promise<IRemoteAgentEnvironment | null> { return null; }
|
||||
async getDiagnosticInfo(options: IDiagnosticInfoOptions): Promise<IDiagnosticInfo | undefined> { return undefined; }
|
||||
async disableTelemetry(): Promise<void> { }
|
||||
async logTelemetry(eventName: string, data?: ITelemetryData): Promise<void> { }
|
||||
async flushTelemetry(): Promise<void> { }
|
||||
async getRawEnvironment(): Promise<IRemoteAgentEnvironment | null> { return null; }
|
||||
async scanExtensions(skipExtensions?: ExtensionIdentifier[]): Promise<IExtensionDescription[]> { return []; }
|
||||
async scanSingleExtension(extensionLocation: URI, isBuiltin: boolean): Promise<IExtensionDescription | null> { return null; }
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
|
||||
//#region Backup File
|
||||
|
||||
class SimpleBackupFileService implements IBackupFileService {
|
||||
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
async hasBackups(): Promise<boolean> { return false; }
|
||||
async discardResourceBackup(resource: URI): Promise<void> { }
|
||||
async discardAllWorkspaceBackups(): Promise<void> { }
|
||||
toBackupResource(resource: URI): URI { return resource; }
|
||||
hasBackupSync(resource: URI, versionId?: number): boolean { return false; }
|
||||
async getBackups(): Promise<URI[]> { return []; }
|
||||
async resolve<T extends object>(resource: URI): Promise<IResolvedBackup<T> | undefined> { return undefined; }
|
||||
async backup<T extends object>(resource: URI, content?: ITextSnapshot, versionId?: number, meta?: T): Promise<void> { }
|
||||
async discardBackup(resource: URI): Promise<void> { }
|
||||
async discardBackups(): Promise<void> { }
|
||||
}
|
||||
|
||||
registerSingleton(IBackupFileService, SimpleBackupFileService);
|
||||
|
||||
//#endregion
|
||||
|
||||
|
||||
//#region Extensions
|
||||
|
||||
class SimpleExtensionService extends NullExtensionService { }
|
||||
|
||||
registerSingleton(IExtensionService, SimpleExtensionService);
|
||||
|
||||
//#endregion
|
||||
|
||||
|
||||
//#region Telemetry
|
||||
|
||||
class SimpleTelemetryService implements ITelemetryService {
|
||||
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
readonly sendErrorTelemetry = false;
|
||||
readonly isOptedIn = false;
|
||||
|
||||
async publicLog(eventName: string, data?: ITelemetryData, anonymizeFilePaths?: boolean): Promise<void> { }
|
||||
async publicLog2<E extends ClassifiedEvent<T> = never, T extends GDPRClassification<T> = never>(eventName: string, data?: StrictPropertyChecker<E, ClassifiedEvent<T>, 'Type of classified event does not match event properties'>, anonymizeFilePaths?: boolean): Promise<void> { }
|
||||
async publicLogError(errorEventName: string, data?: ITelemetryData): Promise<void> { }
|
||||
async publicLogError2<E extends ClassifiedEvent<T> = never, T extends GDPRClassification<T> = never>(eventName: string, data?: StrictPropertyChecker<E, ClassifiedEvent<T>, 'Type of classified event does not match event properties'>): Promise<void> { }
|
||||
setEnabled(value: boolean): void { }
|
||||
setExperimentProperty(name: string, value: string): void { }
|
||||
async getTelemetryInfo(): Promise<ITelemetryInfo> {
|
||||
return {
|
||||
instanceId: 'someValue.instanceId',
|
||||
sessionId: 'someValue.sessionId',
|
||||
machineId: 'someValue.machineId'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
registerSingleton(ITelemetryService, SimpleTelemetryService);
|
||||
|
||||
//#endregion
|
||||
|
||||
|
||||
//#region Keymap Service
|
||||
|
||||
class SimpleKeyboardMapper implements IKeyboardMapper {
|
||||
dumpDebugInfo(): string { return ''; }
|
||||
resolveKeybinding(keybinding: ChordKeybinding): ResolvedKeybinding[] { return []; }
|
||||
resolveKeyboardEvent(keyboardEvent: IKeyboardEvent): ResolvedKeybinding {
|
||||
let keybinding = new SimpleKeybinding(
|
||||
keyboardEvent.ctrlKey,
|
||||
keyboardEvent.shiftKey,
|
||||
keyboardEvent.altKey,
|
||||
keyboardEvent.metaKey,
|
||||
keyboardEvent.keyCode
|
||||
).toChord();
|
||||
return new USLayoutResolvedKeybinding(keybinding, OS);
|
||||
}
|
||||
resolveUserBinding(firstPart: (SimpleKeybinding | ScanCodeBinding)[]): ResolvedKeybinding[] { return []; }
|
||||
}
|
||||
|
||||
class SimpleKeymapService implements IKeymapService {
|
||||
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
onDidChangeKeyboardMapper = Event.None;
|
||||
getKeyboardMapper(dispatchConfig: DispatchConfig): IKeyboardMapper { return new SimpleKeyboardMapper(); }
|
||||
getCurrentKeyboardLayout(): (IWindowsKeyboardLayoutInfo & { isUserKeyboardLayout?: boolean | undefined; isUSStandard?: true | undefined; }) | (ILinuxKeyboardLayoutInfo & { isUserKeyboardLayout?: boolean | undefined; isUSStandard?: true | undefined; }) | (IMacKeyboardLayoutInfo & { isUserKeyboardLayout?: boolean | undefined; isUSStandard?: true | undefined; }) | null { return null; }
|
||||
getAllKeyboardLayouts(): IKeyboardLayoutInfo[] { return []; }
|
||||
getRawKeyboardMapping(): IWindowsKeyboardMapping | ILinuxKeyboardMapping | IMacKeyboardMapping | null { return null; }
|
||||
validateCurrentKeyboardMapping(keyboardEvent: IKeyboardEvent): void { }
|
||||
}
|
||||
|
||||
registerSingleton(IKeymapService, SimpleKeymapService);
|
||||
|
||||
//#endregion
|
||||
|
||||
|
||||
//#region Webview
|
||||
|
||||
class SimpleWebviewService implements IWebviewService {
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
readonly activeWebview = undefined;
|
||||
|
||||
createWebviewElement(id: string, options: WebviewOptions, contentOptions: WebviewContentOptions, extension: WebviewExtensionDescription | undefined): WebviewElement { throw new Error('Method not implemented.'); }
|
||||
createWebviewOverlay(id: string, options: WebviewOptions, contentOptions: WebviewContentOptions, extension: WebviewExtensionDescription | undefined): WebviewOverlay { throw new Error('Method not implemented.'); }
|
||||
setIcons(id: string, value: WebviewIcons | undefined): void { }
|
||||
}
|
||||
|
||||
registerSingleton(IWebviewService, SimpleWebviewService);
|
||||
|
||||
//#endregion
|
||||
|
||||
|
||||
//#region Textfiles
|
||||
|
||||
class SimpleTextFileService extends AbstractTextFileService {
|
||||
declare readonly _serviceBrand: undefined;
|
||||
}
|
||||
|
||||
registerSingleton(ITextFileService, SimpleTextFileService);
|
||||
|
||||
//#endregion
|
||||
|
||||
|
||||
//#region extensions management
|
||||
|
||||
class SimpleExtensionManagementServerService implements IExtensionManagementServerService {
|
||||
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
readonly localExtensionManagementServer = null;
|
||||
readonly remoteExtensionManagementServer = null;
|
||||
readonly webExtensionManagementServer = null;
|
||||
|
||||
getExtensionManagementServer(extension: IExtension): IExtensionManagementServer | null { return null; }
|
||||
}
|
||||
|
||||
registerSingleton(IExtensionManagementServerService, SimpleExtensionManagementServerService);
|
||||
|
||||
//#endregion
|
||||
|
||||
|
||||
//#region Tunnel
|
||||
|
||||
class SimpleTunnelService implements ITunnelService {
|
||||
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
tunnels: Promise<readonly RemoteTunnel[]> = Promise.resolve([]);
|
||||
|
||||
onTunnelOpened = Event.None;
|
||||
onTunnelClosed = Event.None;
|
||||
|
||||
openTunnel(addressProvider: IAddressProvider | undefined, remoteHost: string | undefined, remotePort: number, localPort?: number): Promise<RemoteTunnel> | undefined { return undefined; }
|
||||
async closeTunnel(remoteHost: string, remotePort: number): Promise<void> { }
|
||||
setTunnelProvider(provider: ITunnelProvider | undefined): IDisposable { return Disposable.None; }
|
||||
}
|
||||
|
||||
registerSingleton(ITunnelService, SimpleTunnelService);
|
||||
|
||||
//#endregion
|
||||
|
||||
|
||||
//#region User Data Sync
|
||||
|
||||
class SimpleUserDataSyncService implements IUserDataSyncService {
|
||||
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
onDidChangeStatus = Event.None;
|
||||
onDidChangeConflicts = Event.None;
|
||||
onDidChangeLocal = Event.None;
|
||||
onSyncErrors = Event.None;
|
||||
onDidChangeLastSyncTime = Event.None;
|
||||
onDidResetRemote = Event.None;
|
||||
onDidResetLocal = Event.None;
|
||||
|
||||
status: SyncStatus = SyncStatus.Idle;
|
||||
conflicts: [SyncResource, IResourcePreview[]][] = [];
|
||||
lastSyncTime = undefined;
|
||||
|
||||
createSyncTask(): Promise<ISyncTask> { throw new Error('Method not implemented.'); }
|
||||
createManualSyncTask(): Promise<IManualSyncTask> { throw new Error('Method not implemented.'); }
|
||||
|
||||
async replace(uri: URI): Promise<void> { }
|
||||
async reset(): Promise<void> { }
|
||||
async resetRemote(): Promise<void> { }
|
||||
async resetLocal(): Promise<void> { }
|
||||
async hasLocalData(): Promise<boolean> { return false; }
|
||||
async hasPreviouslySynced(): Promise<boolean> { return false; }
|
||||
async resolveContent(resource: URI): Promise<string | null> { return null; }
|
||||
async accept(resource: SyncResource, conflictResource: URI, content: string | null | undefined, apply: boolean): Promise<void> { }
|
||||
async getLocalSyncResourceHandles(resource: SyncResource): Promise<ISyncResourceHandle[]> { return []; }
|
||||
async getRemoteSyncResourceHandles(resource: SyncResource): Promise<ISyncResourceHandle[]> { return []; }
|
||||
async getAssociatedResources(resource: SyncResource, syncResourceHandle: ISyncResourceHandle): Promise<{ resource: URI; comparableResource: URI; }[]> { return []; }
|
||||
async getMachineId(resource: SyncResource, syncResourceHandle: ISyncResourceHandle): Promise<string | undefined> { return undefined; }
|
||||
}
|
||||
|
||||
registerSingleton(IUserDataSyncService, SimpleUserDataSyncService);
|
||||
|
||||
//#endregion
|
||||
|
||||
|
||||
//#region User Data Sync Account
|
||||
|
||||
class SimpleUserDataSyncAccountService implements IUserDataSyncAccountService {
|
||||
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
onTokenFailed = Event.None;
|
||||
onDidChangeAccount = Event.None;
|
||||
|
||||
account: IUserDataSyncAccount | undefined = undefined;
|
||||
|
||||
async updateAccount(account: IUserDataSyncAccount | undefined): Promise<void> { }
|
||||
}
|
||||
|
||||
registerSingleton(IUserDataSyncAccountService, SimpleUserDataSyncAccountService);
|
||||
|
||||
//#endregion
|
||||
|
||||
|
||||
//#region User Data Auto Sync Account
|
||||
|
||||
class SimpleUserDataAutoSyncAccountService implements IUserDataAutoSyncService {
|
||||
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
onError = Event.None;
|
||||
onDidChangeEnablement = Event.None;
|
||||
|
||||
isEnabled(): boolean { return false; }
|
||||
canToggleEnablement(): boolean { return false; }
|
||||
async turnOn(): Promise<void> { }
|
||||
async turnOff(everywhere: boolean): Promise<void> { }
|
||||
async triggerSync(sources: string[], hasToLimitSync: boolean, disableCache: boolean): Promise<void> { }
|
||||
}
|
||||
|
||||
registerSingleton(IUserDataAutoSyncService, SimpleUserDataAutoSyncAccountService);
|
||||
|
||||
//#endregion
|
||||
|
||||
|
||||
//#region User Data Sync Store Management
|
||||
|
||||
class SimpleIUserDataSyncStoreManagementService implements IUserDataSyncStoreManagementService {
|
||||
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
onDidChangeUserDataSyncStore = Event.None;
|
||||
|
||||
userDataSyncStore: IUserDataSyncStore | undefined = undefined;
|
||||
|
||||
async switch(type: UserDataSyncStoreType): Promise<void> { }
|
||||
|
||||
async getPreviousUserDataSyncStore(): Promise<IUserDataSyncStore | undefined> { return undefined; }
|
||||
}
|
||||
|
||||
registerSingleton(IUserDataSyncStoreManagementService, SimpleIUserDataSyncStoreManagementService);
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region IStorageKeysSyncRegistryService
|
||||
|
||||
class SimpleIStorageKeysSyncRegistryService implements IStorageKeysSyncRegistryService {
|
||||
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
onDidChangeStorageKeys = Event.None;
|
||||
|
||||
storageKeys = [];
|
||||
|
||||
registerStorageKey(): void { }
|
||||
|
||||
onDidChangeExtensionStorageKeys = Event.None;
|
||||
|
||||
extensionsStorageKeys = [];
|
||||
|
||||
getExtensioStorageKeys() { return undefined; }
|
||||
|
||||
registerExtensionStorageKeys(): void { }
|
||||
}
|
||||
|
||||
registerSingleton(IStorageKeysSyncRegistryService, SimpleIStorageKeysSyncRegistryService);
|
||||
|
||||
//#endregion
|
||||
|
||||
|
||||
//#region Task
|
||||
|
||||
class SimpleTaskService implements ITaskService {
|
||||
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
onDidStateChange = Event.None;
|
||||
supportsMultipleTaskExecutions = false;
|
||||
|
||||
configureAction(): Action { throw new Error('Method not implemented.'); }
|
||||
build(): Promise<ITaskSummary> { throw new Error('Method not implemented.'); }
|
||||
runTest(): Promise<ITaskSummary> { throw new Error('Method not implemented.'); }
|
||||
run(task: CustomTask | ContributedTask | InMemoryTask | undefined, options?: ProblemMatcherRunOptions): Promise<ITaskSummary | undefined> { throw new Error('Method not implemented.'); }
|
||||
inTerminal(): boolean { throw new Error('Method not implemented.'); }
|
||||
isActive(): Promise<boolean> { throw new Error('Method not implemented.'); }
|
||||
getActiveTasks(): Promise<Task[]> { throw new Error('Method not implemented.'); }
|
||||
getBusyTasks(): Promise<Task[]> { throw new Error('Method not implemented.'); }
|
||||
restart(task: Task): void { throw new Error('Method not implemented.'); }
|
||||
terminate(task: Task): Promise<TaskTerminateResponse> { throw new Error('Method not implemented.'); }
|
||||
terminateAll(): Promise<TaskTerminateResponse[]> { throw new Error('Method not implemented.'); }
|
||||
tasks(filter?: TaskFilter): Promise<Task[]> { throw new Error('Method not implemented.'); }
|
||||
taskTypes(): string[] { throw new Error('Method not implemented.'); }
|
||||
getWorkspaceTasks(runSource?: TaskRunSource): Promise<Map<string, WorkspaceFolderTaskResult>> { throw new Error('Method not implemented.'); }
|
||||
readRecentTasks(): Promise<(CustomTask | ContributedTask | InMemoryTask | ConfiguringTask)[]> { throw new Error('Method not implemented.'); }
|
||||
getTask(workspaceFolder: string | IWorkspace | IWorkspaceFolder, alias: string | TaskIdentifier, compareId?: boolean): Promise<CustomTask | ContributedTask | InMemoryTask | undefined> { throw new Error('Method not implemented.'); }
|
||||
tryResolveTask(configuringTask: ConfiguringTask): Promise<CustomTask | ContributedTask | InMemoryTask | undefined> { throw new Error('Method not implemented.'); }
|
||||
getTasksForGroup(group: string): Promise<Task[]> { throw new Error('Method not implemented.'); }
|
||||
getRecentlyUsedTasks(): LinkedMap<string, string> { throw new Error('Method not implemented.'); }
|
||||
migrateRecentTasks(tasks: Task[]): Promise<void> { throw new Error('Method not implemented.'); }
|
||||
createSorter(): TaskSorter { throw new Error('Method not implemented.'); }
|
||||
getTaskDescription(task: CustomTask | ContributedTask | InMemoryTask | ConfiguringTask): string | undefined { throw new Error('Method not implemented.'); }
|
||||
canCustomize(task: CustomTask | ContributedTask): boolean { throw new Error('Method not implemented.'); }
|
||||
customize(task: CustomTask | ContributedTask | ConfiguringTask, properties?: {}, openConfig?: boolean): Promise<void> { throw new Error('Method not implemented.'); }
|
||||
openConfig(task: CustomTask | ConfiguringTask | undefined): Promise<boolean> { throw new Error('Method not implemented.'); }
|
||||
registerTaskProvider(taskProvider: ITaskProvider, type: string): IDisposable { throw new Error('Method not implemented.'); }
|
||||
registerTaskSystem(scheme: string, taskSystemInfo: TaskSystemInfo): void { throw new Error('Method not implemented.'); }
|
||||
registerSupportedExecutions(custom?: boolean, shell?: boolean, process?: boolean): void { throw new Error('Method not implemented.'); }
|
||||
setJsonTasksSupported(areSuppored: Promise<boolean>): void { throw new Error('Method not implemented.'); }
|
||||
extensionCallbackTaskComplete(task: Task, result: number | undefined): Promise<void> { throw new Error('Method not implemented.'); }
|
||||
}
|
||||
|
||||
registerSingleton(ITaskService, SimpleTaskService);
|
||||
|
||||
//#endregion
|
||||
|
||||
|
||||
//#region Extension Tips
|
||||
|
||||
class SimpleExtensionTipsService implements IExtensionTipsService {
|
||||
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
onRecommendationChange = Event.None;
|
||||
|
||||
async getConfigBasedTips(folder: URI): Promise<IConfigBasedExtensionTip[]> { return []; }
|
||||
async getImportantExecutableBasedTips(): Promise<IExecutableBasedExtensionTip[]> { return []; }
|
||||
async getOtherExecutableBasedTips(): Promise<IExecutableBasedExtensionTip[]> { return []; }
|
||||
async getAllWorkspacesTips(): Promise<IWorkspaceTips[]> { return []; }
|
||||
}
|
||||
|
||||
registerSingleton(IExtensionTipsService, SimpleExtensionTipsService);
|
||||
|
||||
//#endregion
|
||||
|
||||
|
||||
//#region Workspace Tags
|
||||
|
||||
class SimpleWorkspaceTagsService implements IWorkspaceTagsService {
|
||||
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
async getTags(): Promise<Tags> { return Object.create(null); }
|
||||
getTelemetryWorkspaceId(workspace: IWorkspace, state: WorkbenchState): string | undefined { return undefined; }
|
||||
async getHashedRemotesFromUri(workspaceUri: URI, stripEndingDotGit?: boolean): Promise<string[]> { return []; }
|
||||
}
|
||||
|
||||
registerSingleton(IWorkspaceTagsService, SimpleWorkspaceTagsService);
|
||||
|
||||
//#endregion
|
||||
|
||||
|
||||
//#region Output Channel
|
||||
|
||||
class SimpleOutputChannelModelService extends AsbtractOutputChannelModelService {
|
||||
declare readonly _serviceBrand: undefined;
|
||||
}
|
||||
|
||||
registerSingleton(IOutputChannelModelService, SimpleOutputChannelModelService);
|
||||
|
||||
//#endregion
|
||||
|
||||
|
||||
//#region Integrity
|
||||
|
||||
class SimpleIntegrityService implements IIntegrityService {
|
||||
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
async isPure(): Promise<IntegrityTestResult> {
|
||||
return { isPure: true, proof: [] };
|
||||
}
|
||||
}
|
||||
|
||||
registerSingleton(IIntegrityService, SimpleIntegrityService);
|
||||
|
||||
//#endregion
|
||||
851
lib/vscode/src/vs/workbench/electron-sandbox/window.ts
Normal file
851
lib/vscode/src/vs/workbench/electron-sandbox/window.ts
Normal file
@@ -0,0 +1,851 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import { equals } from 'vs/base/common/objects';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { IAction, Separator } from 'vs/base/common/actions';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { EditorResourceAccessor, IUntitledTextResourceEditorInput, SideBySideEditor, pathsToEditors } from 'vs/workbench/common/editor';
|
||||
import { IEditorService, IResourceEditorInputType } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { WindowMinimumSize, IOpenFileRequest, IWindowsConfiguration, getTitleBarStyle, IAddFoldersRequest, INativeRunActionInWindowRequest, INativeRunKeybindingInWindowRequest, INativeOpenFileRequest } from 'vs/platform/windows/common/windows';
|
||||
import { ITitleService } from 'vs/workbench/services/title/common/titleService';
|
||||
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { applyZoom } from 'vs/platform/windows/electron-sandbox/window';
|
||||
import { setFullscreen, getZoomLevel } from 'vs/base/browser/browser';
|
||||
import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { IResourceEditorInput } from 'vs/platform/editor/common/editor';
|
||||
import { ipcRenderer } from 'vs/base/parts/sandbox/electron-sandbox/globals';
|
||||
import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/common/workspaceEditing';
|
||||
import { IMenuService, MenuId, IMenu, MenuItemAction, ICommandAction, SubmenuItemAction, MenuRegistry } from 'vs/platform/actions/common/actions';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { LifecyclePhase, ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle';
|
||||
import { IWorkspaceFolderCreationData, IWorkspacesService } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { IIntegrityService } from 'vs/workbench/services/integrity/common/integrity';
|
||||
import { isWindows, isMacintosh } from 'vs/base/common/platform';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService';
|
||||
import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility';
|
||||
import { WorkbenchState, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { coalesce } from 'vs/base/common/arrays';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { MenubarControl } from '../browser/parts/titlebar/menubarControl';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { IUpdateService } from 'vs/platform/update/common/update';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { IPreferencesService } from '../services/preferences/common/preferences';
|
||||
import { IMenubarData, IMenubarMenu, IMenubarKeybinding, IMenubarMenuItemSubmenu, IMenubarMenuItemAction, MenubarMenuItem } from 'vs/platform/menubar/common/menubar';
|
||||
import { IMenubarService } from 'vs/platform/menubar/electron-sandbox/menubar';
|
||||
import { withNullAsUndefined, assertIsDefined } from 'vs/base/common/types';
|
||||
import { IOpenerService, OpenOptions } from 'vs/platform/opener/common/opener';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { INativeHostService } from 'vs/platform/native/electron-sandbox/native';
|
||||
import { posix, dirname } from 'vs/base/common/path';
|
||||
import { getBaseLabel } from 'vs/base/common/labels';
|
||||
import { ITunnelService, extractLocalHostUriMetaDataForPortMapping } from 'vs/platform/remote/common/tunnel';
|
||||
import { IWorkbenchLayoutService, Parts, positionFromString, Position } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { IHostService } from 'vs/workbench/services/host/browser/host';
|
||||
import { IWorkingCopyService, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopyService';
|
||||
import { AutoSaveMode, IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { clearAllFontInfos } from 'vs/editor/browser/config/configuration';
|
||||
import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver';
|
||||
import { IAddressProvider, IAddress } from 'vs/platform/remote/common/remoteAgentConnection';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { AuthInfo } from 'vs/base/parts/sandbox/electron-sandbox/electronTypes';
|
||||
|
||||
export class NativeWindow extends Disposable {
|
||||
|
||||
private static REMEMBER_PROXY_CREDENTIALS_KEY = 'window.rememberProxyCredentials';
|
||||
|
||||
private touchBarMenu: IMenu | undefined;
|
||||
private readonly touchBarDisposables = this._register(new DisposableStore());
|
||||
private lastInstalledTouchedBar: ICommandAction[][] | undefined;
|
||||
|
||||
private readonly customTitleContextMenuDisposable = this._register(new DisposableStore());
|
||||
|
||||
private previousConfiguredZoomLevel: number | undefined;
|
||||
|
||||
private readonly addFoldersScheduler = this._register(new RunOnceScheduler(() => this.doAddFolders(), 100));
|
||||
private pendingFoldersToAdd: URI[] = [];
|
||||
|
||||
private readonly closeEmptyWindowScheduler = this._register(new RunOnceScheduler(() => this.onAllEditorsClosed(), 50));
|
||||
|
||||
private isDocumentedEdited = false;
|
||||
|
||||
constructor(
|
||||
@IEditorService private readonly editorService: IEditorService,
|
||||
@IEditorGroupsService private readonly editorGroupService: IEditorGroupsService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@ITitleService private readonly titleService: ITitleService,
|
||||
@IWorkbenchThemeService protected themeService: IWorkbenchThemeService,
|
||||
@INotificationService private readonly notificationService: INotificationService,
|
||||
@ICommandService private readonly commandService: ICommandService,
|
||||
@IKeybindingService private readonly keybindingService: IKeybindingService,
|
||||
@ITelemetryService private readonly telemetryService: ITelemetryService,
|
||||
@IWorkspaceEditingService private readonly workspaceEditingService: IWorkspaceEditingService,
|
||||
@IFileService private readonly fileService: IFileService,
|
||||
@IMenuService private readonly menuService: IMenuService,
|
||||
@ILifecycleService private readonly lifecycleService: ILifecycleService,
|
||||
@IIntegrityService private readonly integrityService: IIntegrityService,
|
||||
@INativeWorkbenchEnvironmentService private readonly environmentService: INativeWorkbenchEnvironmentService,
|
||||
@IAccessibilityService private readonly accessibilityService: IAccessibilityService,
|
||||
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IOpenerService private readonly openerService: IOpenerService,
|
||||
@INativeHostService private readonly nativeHostService: INativeHostService,
|
||||
@ITunnelService private readonly tunnelService: ITunnelService,
|
||||
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
|
||||
@IWorkingCopyService private readonly workingCopyService: IWorkingCopyService,
|
||||
@IFilesConfigurationService private readonly filesConfigurationService: IFilesConfigurationService,
|
||||
@IProductService private readonly productService: IProductService,
|
||||
@IRemoteAuthorityResolverService private readonly remoteAuthorityResolverService: IRemoteAuthorityResolverService,
|
||||
@IDialogService private readonly dialogService: IDialogService,
|
||||
@IStorageService private readonly storageService: IStorageService
|
||||
) {
|
||||
super();
|
||||
|
||||
this.registerListeners();
|
||||
this.create();
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
|
||||
// React to editor input changes
|
||||
this._register(this.editorService.onDidActiveEditorChange(() => this.updateTouchbarMenu()));
|
||||
|
||||
// prevent opening a real URL inside the shell
|
||||
[DOM.EventType.DRAG_OVER, DOM.EventType.DROP].forEach(event => {
|
||||
window.document.body.addEventListener(event, (e: DragEvent) => {
|
||||
DOM.EventHelper.stop(e);
|
||||
});
|
||||
});
|
||||
|
||||
// Support runAction event
|
||||
ipcRenderer.on('vscode:runAction', async (event: unknown, request: INativeRunActionInWindowRequest) => {
|
||||
const args: unknown[] = request.args || [];
|
||||
|
||||
// If we run an action from the touchbar, we fill in the currently active resource
|
||||
// as payload because the touch bar items are context aware depending on the editor
|
||||
if (request.from === 'touchbar') {
|
||||
const activeEditor = this.editorService.activeEditor;
|
||||
if (activeEditor) {
|
||||
const resource = EditorResourceAccessor.getOriginalUri(activeEditor, { supportSideBySide: SideBySideEditor.PRIMARY });
|
||||
if (resource) {
|
||||
args.push(resource);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
args.push({ from: request.from });
|
||||
}
|
||||
|
||||
try {
|
||||
await this.commandService.executeCommand(request.id, ...args);
|
||||
|
||||
type CommandExecutedClassifcation = {
|
||||
id: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
|
||||
from: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
|
||||
};
|
||||
this.telemetryService.publicLog2<{ id: String, from: String }, CommandExecutedClassifcation>('commandExecuted', { id: request.id, from: request.from });
|
||||
} catch (error) {
|
||||
this.notificationService.error(error);
|
||||
}
|
||||
});
|
||||
|
||||
// Support runKeybinding event
|
||||
ipcRenderer.on('vscode:runKeybinding', (event: unknown, request: INativeRunKeybindingInWindowRequest) => {
|
||||
if (document.activeElement) {
|
||||
this.keybindingService.dispatchByUserSettingsLabel(request.userSettingsLabel, document.activeElement);
|
||||
}
|
||||
});
|
||||
|
||||
// Error reporting from main
|
||||
ipcRenderer.on('vscode:reportError', (event: unknown, error: string) => {
|
||||
if (error) {
|
||||
errors.onUnexpectedError(JSON.parse(error));
|
||||
}
|
||||
});
|
||||
|
||||
// Support openFiles event for existing and new files
|
||||
ipcRenderer.on('vscode:openFiles', (event: unknown, request: IOpenFileRequest) => this.onOpenFiles(request));
|
||||
|
||||
// Support addFolders event if we have a workspace opened
|
||||
ipcRenderer.on('vscode:addFolders', (event: unknown, request: IAddFoldersRequest) => this.onAddFoldersRequest(request));
|
||||
|
||||
// Message support
|
||||
ipcRenderer.on('vscode:showInfoMessage', (event: unknown, message: string) => {
|
||||
this.notificationService.info(message);
|
||||
});
|
||||
|
||||
// Display change events
|
||||
ipcRenderer.on('vscode:displayChanged', () => {
|
||||
clearAllFontInfos();
|
||||
});
|
||||
|
||||
// Fullscreen Events
|
||||
ipcRenderer.on('vscode:enterFullScreen', async () => {
|
||||
await this.lifecycleService.when(LifecyclePhase.Ready);
|
||||
setFullscreen(true);
|
||||
});
|
||||
|
||||
ipcRenderer.on('vscode:leaveFullScreen', async () => {
|
||||
await this.lifecycleService.when(LifecyclePhase.Ready);
|
||||
setFullscreen(false);
|
||||
});
|
||||
|
||||
// Proxy Login Dialog
|
||||
ipcRenderer.on('vscode:openProxyAuthenticationDialog', async (event: unknown, payload: { authInfo: AuthInfo, username?: string, password?: string, replyChannel: string }) => {
|
||||
const rememberCredentials = this.storageService.getBoolean(NativeWindow.REMEMBER_PROXY_CREDENTIALS_KEY, StorageScope.GLOBAL);
|
||||
const result = await this.dialogService.input(Severity.Warning, nls.localize('proxyAuthRequired', "Proxy Authentication Required"),
|
||||
[
|
||||
nls.localize({ key: 'loginButton', comment: ['&& denotes a mnemonic'] }, "&&Log In"),
|
||||
nls.localize({ key: 'cancelButton', comment: ['&& denotes a mnemonic'] }, "&&Cancel")
|
||||
],
|
||||
[
|
||||
{ placeholder: nls.localize('username', "Username"), value: payload.username },
|
||||
{ placeholder: nls.localize('password', "Password"), type: 'password', value: payload.password }
|
||||
],
|
||||
{
|
||||
cancelId: 1,
|
||||
detail: nls.localize('proxyDetail', "The proxy {0} requires a username and password.", `${payload.authInfo.host}:${payload.authInfo.port}`),
|
||||
checkbox: {
|
||||
label: nls.localize('rememberCredentials', "Remember my credentials"),
|
||||
checked: rememberCredentials
|
||||
}
|
||||
});
|
||||
|
||||
// Reply back to the channel without result to indicate
|
||||
// that the login dialog was cancelled
|
||||
if (result.choice !== 0 || !result.values) {
|
||||
ipcRenderer.send(payload.replyChannel);
|
||||
}
|
||||
|
||||
// Other reply back with the picked credentials
|
||||
else {
|
||||
|
||||
// Update state based on checkbox
|
||||
if (result.checkboxChecked) {
|
||||
this.storageService.store(NativeWindow.REMEMBER_PROXY_CREDENTIALS_KEY, true, StorageScope.GLOBAL);
|
||||
} else {
|
||||
this.storageService.remove(NativeWindow.REMEMBER_PROXY_CREDENTIALS_KEY, StorageScope.GLOBAL);
|
||||
}
|
||||
|
||||
// Reply back to main side with credentials
|
||||
const [username, password] = result.values;
|
||||
ipcRenderer.send(payload.replyChannel, { username, password, remember: !!result.checkboxChecked });
|
||||
}
|
||||
});
|
||||
|
||||
// Accessibility support changed event
|
||||
ipcRenderer.on('vscode:accessibilitySupportChanged', (event: unknown, accessibilitySupportEnabled: boolean) => {
|
||||
this.accessibilityService.setAccessibilitySupport(accessibilitySupportEnabled ? AccessibilitySupport.Enabled : AccessibilitySupport.Disabled);
|
||||
});
|
||||
|
||||
// Zoom level changes
|
||||
this.updateWindowZoomLevel();
|
||||
this._register(this.configurationService.onDidChangeConfiguration(e => {
|
||||
if (e.affectsConfiguration('window.zoomLevel')) {
|
||||
this.updateWindowZoomLevel();
|
||||
} else if (e.affectsConfiguration('keyboard.touchbar.enabled') || e.affectsConfiguration('keyboard.touchbar.ignored')) {
|
||||
this.updateTouchbarMenu();
|
||||
}
|
||||
}));
|
||||
|
||||
// Listen to visible editor changes
|
||||
this._register(this.editorService.onDidVisibleEditorsChange(() => this.onDidVisibleEditorsChange()));
|
||||
|
||||
// Listen to editor closing (if we run with --wait)
|
||||
const filesToWait = this.environmentService.configuration.filesToWait;
|
||||
if (filesToWait) {
|
||||
this.trackClosedWaitFiles(filesToWait.waitMarkerFileUri, coalesce(filesToWait.paths.map(path => path.fileUri)));
|
||||
}
|
||||
|
||||
// macOS OS integration
|
||||
if (isMacintosh) {
|
||||
this._register(this.editorService.onDidActiveEditorChange(() => {
|
||||
const file = EditorResourceAccessor.getOriginalUri(this.editorService.activeEditor, { supportSideBySide: SideBySideEditor.PRIMARY, filterByScheme: Schemas.file });
|
||||
|
||||
// Represented Filename
|
||||
this.updateRepresentedFilename(file?.fsPath);
|
||||
|
||||
// Custom title menu
|
||||
this.provideCustomTitleContextMenu(file?.fsPath);
|
||||
}));
|
||||
}
|
||||
|
||||
// Maximize/Restore on doubleclick (for macOS custom title)
|
||||
if (isMacintosh && getTitleBarStyle(this.configurationService, this.environmentService) === 'custom') {
|
||||
const titlePart = assertIsDefined(this.layoutService.getContainer(Parts.TITLEBAR_PART));
|
||||
|
||||
this._register(DOM.addDisposableListener(titlePart, DOM.EventType.DBLCLICK, e => {
|
||||
DOM.EventHelper.stop(e);
|
||||
|
||||
this.nativeHostService.handleTitleDoubleClick();
|
||||
}));
|
||||
}
|
||||
|
||||
// Document edited: indicate for dirty working copies
|
||||
this._register(this.workingCopyService.onDidChangeDirty(workingCopy => {
|
||||
const gotDirty = workingCopy.isDirty();
|
||||
if (gotDirty && !(workingCopy.capabilities & WorkingCopyCapabilities.Untitled) && this.filesConfigurationService.getAutoSaveMode() === AutoSaveMode.AFTER_SHORT_DELAY) {
|
||||
return; // do not indicate dirty of working copies that are auto saved after short delay
|
||||
}
|
||||
|
||||
this.updateDocumentEdited(gotDirty);
|
||||
}));
|
||||
|
||||
this.updateDocumentEdited();
|
||||
|
||||
// Detect minimize / maximize
|
||||
this._register(Event.any(
|
||||
Event.map(Event.filter(this.nativeHostService.onDidMaximizeWindow, id => id === this.nativeHostService.windowId), () => true),
|
||||
Event.map(Event.filter(this.nativeHostService.onDidUnmaximizeWindow, id => id === this.nativeHostService.windowId), () => false)
|
||||
)(e => this.onDidChangeMaximized(e)));
|
||||
|
||||
this.onDidChangeMaximized(this.environmentService.configuration.maximized ?? false);
|
||||
|
||||
// Detect panel position to determine minimum width
|
||||
this._register(this.layoutService.onPanelPositionChange(pos => {
|
||||
this.onDidPanelPositionChange(positionFromString(pos));
|
||||
}));
|
||||
this.onDidPanelPositionChange(this.layoutService.getPanelPosition());
|
||||
}
|
||||
|
||||
private updateDocumentEdited(isDirty = this.workingCopyService.hasDirty): void {
|
||||
if ((!this.isDocumentedEdited && isDirty) || (this.isDocumentedEdited && !isDirty)) {
|
||||
this.isDocumentedEdited = isDirty;
|
||||
|
||||
this.nativeHostService.setDocumentEdited(isDirty);
|
||||
}
|
||||
}
|
||||
|
||||
private onDidChangeMaximized(maximized: boolean): void {
|
||||
this.layoutService.updateWindowMaximizedState(maximized);
|
||||
}
|
||||
|
||||
private getWindowMinimumWidth(panelPosition: Position = this.layoutService.getPanelPosition()): number {
|
||||
// if panel is on the side, then return the larger minwidth
|
||||
const panelOnSide = panelPosition === Position.LEFT || panelPosition === Position.RIGHT;
|
||||
if (panelOnSide) {
|
||||
return WindowMinimumSize.WIDTH_WITH_VERTICAL_PANEL;
|
||||
}
|
||||
else {
|
||||
return WindowMinimumSize.WIDTH;
|
||||
}
|
||||
}
|
||||
|
||||
private onDidPanelPositionChange(pos: Position): void {
|
||||
const minWidth = this.getWindowMinimumWidth(pos);
|
||||
this.nativeHostService.setMinimumSize(minWidth, undefined);
|
||||
}
|
||||
|
||||
private onDidVisibleEditorsChange(): void {
|
||||
|
||||
// Close when empty: check if we should close the window based on the setting
|
||||
// Overruled by: window has a workspace opened or this window is for extension development
|
||||
// or setting is disabled. Also enabled when running with --wait from the command line.
|
||||
const visibleEditorPanes = this.editorService.visibleEditorPanes;
|
||||
if (visibleEditorPanes.length === 0 && this.contextService.getWorkbenchState() === WorkbenchState.EMPTY && !this.environmentService.isExtensionDevelopment) {
|
||||
const closeWhenEmpty = this.configurationService.getValue<boolean>('window.closeWhenEmpty');
|
||||
if (closeWhenEmpty || this.environmentService.args.wait) {
|
||||
this.closeEmptyWindowScheduler.schedule();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private onAllEditorsClosed(): void {
|
||||
const visibleEditorPanes = this.editorService.visibleEditorPanes.length;
|
||||
if (visibleEditorPanes === 0) {
|
||||
this.nativeHostService.closeWindow();
|
||||
}
|
||||
}
|
||||
|
||||
private updateWindowZoomLevel(): void {
|
||||
const windowConfig = this.configurationService.getValue<IWindowsConfiguration>();
|
||||
|
||||
let configuredZoomLevel = 0;
|
||||
if (windowConfig.window && typeof windowConfig.window.zoomLevel === 'number') {
|
||||
configuredZoomLevel = windowConfig.window.zoomLevel;
|
||||
|
||||
// Leave early if the configured zoom level did not change (https://github.com/microsoft/vscode/issues/1536)
|
||||
if (this.previousConfiguredZoomLevel === configuredZoomLevel) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.previousConfiguredZoomLevel = configuredZoomLevel;
|
||||
}
|
||||
|
||||
if (getZoomLevel() !== configuredZoomLevel) {
|
||||
applyZoom(configuredZoomLevel);
|
||||
}
|
||||
}
|
||||
|
||||
private updateRepresentedFilename(filePath: string | undefined): void {
|
||||
this.nativeHostService.setRepresentedFilename(filePath ? filePath : '');
|
||||
}
|
||||
|
||||
private provideCustomTitleContextMenu(filePath: string | undefined): void {
|
||||
|
||||
// Clear old menu
|
||||
this.customTitleContextMenuDisposable.clear();
|
||||
|
||||
// Provide new menu if a file is opened and we are on a custom title
|
||||
if (!filePath || getTitleBarStyle(this.configurationService, this.environmentService) !== 'custom') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Split up filepath into segments
|
||||
const segments = filePath.split(posix.sep);
|
||||
for (let i = segments.length; i > 0; i--) {
|
||||
const isFile = (i === segments.length);
|
||||
|
||||
let pathOffset = i;
|
||||
if (!isFile) {
|
||||
pathOffset++; // for segments which are not the file name we want to open the folder
|
||||
}
|
||||
|
||||
const path = segments.slice(0, pathOffset).join(posix.sep);
|
||||
|
||||
let label: string;
|
||||
if (!isFile) {
|
||||
label = getBaseLabel(dirname(path));
|
||||
} else {
|
||||
label = getBaseLabel(path);
|
||||
}
|
||||
|
||||
const commandId = `workbench.action.revealPathInFinder${i}`;
|
||||
this.customTitleContextMenuDisposable.add(CommandsRegistry.registerCommand(commandId, () => this.nativeHostService.showItemInFolder(path)));
|
||||
this.customTitleContextMenuDisposable.add(MenuRegistry.appendMenuItem(MenuId.TitleBarContext, { command: { id: commandId, title: label || posix.sep }, order: -i }));
|
||||
}
|
||||
}
|
||||
|
||||
private create(): void {
|
||||
|
||||
// Native menu controller
|
||||
if (isMacintosh || getTitleBarStyle(this.configurationService, this.environmentService) === 'native') {
|
||||
this._register(this.instantiationService.createInstance(NativeMenubarControl));
|
||||
}
|
||||
|
||||
// Handle open calls
|
||||
this.setupOpenHandlers();
|
||||
|
||||
// Notify main side when window ready
|
||||
this.lifecycleService.when(LifecyclePhase.Ready).then(() => this.nativeHostService.notifyReady());
|
||||
|
||||
// Integrity warning
|
||||
this.integrityService.isPure().then(res => this.titleService.updateProperties({ isPure: res.isPure }));
|
||||
|
||||
// Root warning
|
||||
this.lifecycleService.when(LifecyclePhase.Restored).then(async () => {
|
||||
const isAdmin = await this.nativeHostService.isAdmin();
|
||||
|
||||
// Update title
|
||||
this.titleService.updateProperties({ isAdmin });
|
||||
|
||||
// Show warning message (unix only)
|
||||
if (isAdmin && !isWindows) {
|
||||
this.notificationService.warn(nls.localize('runningAsRoot', "It is not recommended to run {0} as root user.", this.productService.nameShort));
|
||||
}
|
||||
});
|
||||
|
||||
// Touchbar menu (if enabled)
|
||||
this.updateTouchbarMenu();
|
||||
}
|
||||
|
||||
private setupOpenHandlers(): void {
|
||||
|
||||
// Block window.open() calls
|
||||
window.open = function (): Window | null {
|
||||
throw new Error('Prevented call to window.open(). Use IOpenerService instead!');
|
||||
};
|
||||
|
||||
// Handle external open() calls
|
||||
this.openerService.setExternalOpener({
|
||||
openExternal: async (href: string) => {
|
||||
const success = await this.nativeHostService.openExternal(href);
|
||||
if (!success) {
|
||||
const fileCandidate = URI.parse(href);
|
||||
if (fileCandidate.scheme === Schemas.file) {
|
||||
// if opening failed, and this is a file, we can still try to reveal it
|
||||
await this.nativeHostService.showItemInFolder(fileCandidate.fsPath);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
// Register external URI resolver
|
||||
this.openerService.registerExternalUriResolver({
|
||||
resolveExternalUri: async (uri: URI, options?: OpenOptions) => {
|
||||
if (options?.allowTunneling) {
|
||||
const portMappingRequest = extractLocalHostUriMetaDataForPortMapping(uri);
|
||||
if (portMappingRequest) {
|
||||
const remoteAuthority = this.environmentService.remoteAuthority;
|
||||
const addressProvider: IAddressProvider | undefined = remoteAuthority ? {
|
||||
getAddress: async (): Promise<IAddress> => {
|
||||
return (await this.remoteAuthorityResolverService.resolveAuthority(remoteAuthority)).authority;
|
||||
}
|
||||
} : undefined;
|
||||
const tunnel = await this.tunnelService.openTunnel(addressProvider, portMappingRequest.address, portMappingRequest.port);
|
||||
if (tunnel) {
|
||||
return {
|
||||
resolved: uri.with({ authority: tunnel.localAddress }),
|
||||
dispose: () => tunnel.dispose(),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private updateTouchbarMenu(): void {
|
||||
if (!isMacintosh) {
|
||||
return; // macOS only
|
||||
}
|
||||
|
||||
// Dispose old
|
||||
this.touchBarDisposables.clear();
|
||||
this.touchBarMenu = undefined;
|
||||
|
||||
// Create new (delayed)
|
||||
const scheduler: RunOnceScheduler = this.touchBarDisposables.add(new RunOnceScheduler(() => this.doUpdateTouchbarMenu(scheduler), 300));
|
||||
scheduler.schedule();
|
||||
}
|
||||
|
||||
private doUpdateTouchbarMenu(scheduler: RunOnceScheduler): void {
|
||||
if (!this.touchBarMenu) {
|
||||
const scopedContextKeyService = this.editorService.activeEditorPane?.scopedContextKeyService || this.editorGroupService.activeGroup.scopedContextKeyService;
|
||||
this.touchBarMenu = this.menuService.createMenu(MenuId.TouchBarContext, scopedContextKeyService);
|
||||
this.touchBarDisposables.add(this.touchBarMenu);
|
||||
this.touchBarDisposables.add(this.touchBarMenu.onDidChange(() => scheduler.schedule()));
|
||||
}
|
||||
|
||||
const actions: Array<MenuItemAction | Separator> = [];
|
||||
|
||||
const disabled = this.configurationService.getValue<boolean>('keyboard.touchbar.enabled') === false;
|
||||
const ignoredItems = this.configurationService.getValue<string[]>('keyboard.touchbar.ignored') || [];
|
||||
|
||||
// Fill actions into groups respecting order
|
||||
this.touchBarDisposables.add(createAndFillInActionBarActions(this.touchBarMenu, undefined, actions));
|
||||
|
||||
// Convert into command action multi array
|
||||
const items: ICommandAction[][] = [];
|
||||
let group: ICommandAction[] = [];
|
||||
if (!disabled) {
|
||||
for (const action of actions) {
|
||||
|
||||
// Command
|
||||
if (action instanceof MenuItemAction) {
|
||||
if (ignoredItems.indexOf(action.item.id) >= 0) {
|
||||
continue; // ignored
|
||||
}
|
||||
|
||||
group.push(action.item);
|
||||
}
|
||||
|
||||
// Separator
|
||||
else if (action instanceof Separator) {
|
||||
if (group.length) {
|
||||
items.push(group);
|
||||
}
|
||||
|
||||
group = [];
|
||||
}
|
||||
}
|
||||
|
||||
if (group.length) {
|
||||
items.push(group);
|
||||
}
|
||||
}
|
||||
|
||||
// Only update if the actions have changed
|
||||
if (!equals(this.lastInstalledTouchedBar, items)) {
|
||||
this.lastInstalledTouchedBar = items;
|
||||
this.nativeHostService.updateTouchBar(items);
|
||||
}
|
||||
}
|
||||
|
||||
private onAddFoldersRequest(request: IAddFoldersRequest): void {
|
||||
|
||||
// Buffer all pending requests
|
||||
this.pendingFoldersToAdd.push(...request.foldersToAdd.map(folder => URI.revive(folder)));
|
||||
|
||||
// Delay the adding of folders a bit to buffer in case more requests are coming
|
||||
if (!this.addFoldersScheduler.isScheduled()) {
|
||||
this.addFoldersScheduler.schedule();
|
||||
}
|
||||
}
|
||||
|
||||
private doAddFolders(): void {
|
||||
const foldersToAdd: IWorkspaceFolderCreationData[] = [];
|
||||
|
||||
this.pendingFoldersToAdd.forEach(folder => {
|
||||
foldersToAdd.push(({ uri: folder }));
|
||||
});
|
||||
|
||||
this.pendingFoldersToAdd = [];
|
||||
|
||||
this.workspaceEditingService.addFolders(foldersToAdd);
|
||||
}
|
||||
|
||||
private async onOpenFiles(request: INativeOpenFileRequest): Promise<void> {
|
||||
const inputs: IResourceEditorInputType[] = [];
|
||||
const diffMode = !!(request.filesToDiff && (request.filesToDiff.length === 2));
|
||||
|
||||
if (!diffMode && request.filesToOpenOrCreate) {
|
||||
inputs.push(...(await pathsToEditors(request.filesToOpenOrCreate, this.fileService)));
|
||||
}
|
||||
|
||||
if (diffMode && request.filesToDiff) {
|
||||
inputs.push(...(await pathsToEditors(request.filesToDiff, this.fileService)));
|
||||
}
|
||||
|
||||
if (inputs.length) {
|
||||
this.openResources(inputs, diffMode);
|
||||
}
|
||||
|
||||
if (request.filesToWait && inputs.length) {
|
||||
// In wait mode, listen to changes to the editors and wait until the files
|
||||
// are closed that the user wants to wait for. When this happens we delete
|
||||
// the wait marker file to signal to the outside that editing is done.
|
||||
this.trackClosedWaitFiles(URI.revive(request.filesToWait.waitMarkerFileUri), coalesce(request.filesToWait.paths.map(p => URI.revive(p.fileUri))));
|
||||
}
|
||||
}
|
||||
|
||||
private async trackClosedWaitFiles(waitMarkerFile: URI, resourcesToWaitFor: URI[]): Promise<void> {
|
||||
|
||||
// Wait for the resources to be closed in the editor...
|
||||
await this.editorService.whenClosed(resourcesToWaitFor.map(resource => ({ resource })), { waitForSaved: true });
|
||||
|
||||
// ...before deleting the wait marker file
|
||||
await this.fileService.del(waitMarkerFile);
|
||||
}
|
||||
|
||||
private async openResources(resources: Array<IResourceEditorInput | IUntitledTextResourceEditorInput>, diffMode: boolean): Promise<unknown> {
|
||||
await this.lifecycleService.when(LifecyclePhase.Ready);
|
||||
|
||||
// In diffMode we open 2 resources as diff
|
||||
if (diffMode && resources.length === 2 && resources[0].resource && resources[1].resource) {
|
||||
return this.editorService.openEditor({ leftResource: resources[0].resource, rightResource: resources[1].resource, options: { pinned: true } });
|
||||
}
|
||||
|
||||
// For one file, just put it into the current active editor
|
||||
if (resources.length === 1) {
|
||||
return this.editorService.openEditor(resources[0]);
|
||||
}
|
||||
|
||||
// Otherwise open all
|
||||
return this.editorService.openEditors(resources);
|
||||
}
|
||||
}
|
||||
|
||||
class NativeMenubarControl extends MenubarControl {
|
||||
constructor(
|
||||
@IMenuService menuService: IMenuService,
|
||||
@IWorkspacesService workspacesService: IWorkspacesService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@ILabelService labelService: ILabelService,
|
||||
@IUpdateService updateService: IUpdateService,
|
||||
@IStorageService storageService: IStorageService,
|
||||
@INotificationService notificationService: INotificationService,
|
||||
@IPreferencesService preferencesService: IPreferencesService,
|
||||
@INativeWorkbenchEnvironmentService protected readonly environmentService: INativeWorkbenchEnvironmentService,
|
||||
@IAccessibilityService accessibilityService: IAccessibilityService,
|
||||
@IMenubarService private readonly menubarService: IMenubarService,
|
||||
@IHostService hostService: IHostService,
|
||||
@INativeHostService private readonly nativeHostService: INativeHostService
|
||||
) {
|
||||
super(
|
||||
menuService,
|
||||
workspacesService,
|
||||
contextKeyService,
|
||||
keybindingService,
|
||||
configurationService,
|
||||
labelService,
|
||||
updateService,
|
||||
storageService,
|
||||
notificationService,
|
||||
preferencesService,
|
||||
environmentService,
|
||||
accessibilityService,
|
||||
hostService
|
||||
);
|
||||
|
||||
if (isMacintosh) {
|
||||
this.menus['Preferences'] = this._register(this.menuService.createMenu(MenuId.MenubarPreferencesMenu, this.contextKeyService));
|
||||
this.topLevelTitles['Preferences'] = nls.localize('mPreferences', "Preferences");
|
||||
}
|
||||
|
||||
for (const topLevelMenuName of Object.keys(this.topLevelTitles)) {
|
||||
const menu = this.menus[topLevelMenuName];
|
||||
if (menu) {
|
||||
this._register(menu.onDidChange(() => this.updateMenubar()));
|
||||
}
|
||||
}
|
||||
|
||||
(async () => {
|
||||
this.recentlyOpened = await this.workspacesService.getRecentlyOpened();
|
||||
|
||||
this.doUpdateMenubar();
|
||||
})();
|
||||
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
protected doUpdateMenubar(): void {
|
||||
// Since the native menubar is shared between windows (main process)
|
||||
// only allow the focused window to update the menubar
|
||||
if (!this.hostService.hasFocus) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Send menus to main process to be rendered by Electron
|
||||
const menubarData = { menus: {}, keybindings: {} };
|
||||
if (this.getMenubarMenus(menubarData)) {
|
||||
this.menubarService.updateMenubar(this.nativeHostService.windowId, menubarData);
|
||||
}
|
||||
}
|
||||
|
||||
private getMenubarMenus(menubarData: IMenubarData): boolean {
|
||||
if (!menubarData) {
|
||||
return false;
|
||||
}
|
||||
|
||||
menubarData.keybindings = this.getAdditionalKeybindings();
|
||||
for (const topLevelMenuName of Object.keys(this.topLevelTitles)) {
|
||||
const menu = this.menus[topLevelMenuName];
|
||||
if (menu) {
|
||||
const menubarMenu: IMenubarMenu = { items: [] };
|
||||
this.populateMenuItems(menu, menubarMenu, menubarData.keybindings);
|
||||
if (menubarMenu.items.length === 0) {
|
||||
return false; // Menus are incomplete
|
||||
}
|
||||
menubarData.menus[topLevelMenuName] = menubarMenu;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private populateMenuItems(menu: IMenu, menuToPopulate: IMenubarMenu, keybindings: { [id: string]: IMenubarKeybinding | undefined }) {
|
||||
let groups = menu.getActions();
|
||||
for (let group of groups) {
|
||||
const [, actions] = group;
|
||||
|
||||
actions.forEach(menuItem => {
|
||||
|
||||
if (menuItem instanceof SubmenuItemAction) {
|
||||
const submenu = { items: [] };
|
||||
|
||||
if (!this.menus[menuItem.item.submenu.id]) {
|
||||
const menu = this.menus[menuItem.item.submenu.id] = this.menuService.createMenu(menuItem.item.submenu, this.contextKeyService);
|
||||
this._register(menu.onDidChange(() => this.updateMenubar()));
|
||||
}
|
||||
|
||||
const menuToDispose = this.menuService.createMenu(menuItem.item.submenu, this.contextKeyService);
|
||||
this.populateMenuItems(menuToDispose, submenu, keybindings);
|
||||
|
||||
let menubarSubmenuItem: IMenubarMenuItemSubmenu = {
|
||||
id: menuItem.id,
|
||||
label: menuItem.label,
|
||||
submenu: submenu
|
||||
};
|
||||
|
||||
menuToPopulate.items.push(menubarSubmenuItem);
|
||||
menuToDispose.dispose();
|
||||
} else {
|
||||
if (menuItem.id === 'workbench.action.openRecent') {
|
||||
const actions = this.getOpenRecentActions().map(this.transformOpenRecentAction);
|
||||
menuToPopulate.items.push(...actions);
|
||||
}
|
||||
|
||||
let menubarMenuItem: IMenubarMenuItemAction = {
|
||||
id: menuItem.id,
|
||||
label: menuItem.label
|
||||
};
|
||||
|
||||
if (menuItem.checked) {
|
||||
menubarMenuItem.checked = true;
|
||||
}
|
||||
|
||||
if (!menuItem.enabled) {
|
||||
menubarMenuItem.enabled = false;
|
||||
}
|
||||
|
||||
menubarMenuItem.label = this.calculateActionLabel(menubarMenuItem);
|
||||
keybindings[menuItem.id] = this.getMenubarKeybinding(menuItem.id);
|
||||
menuToPopulate.items.push(menubarMenuItem);
|
||||
}
|
||||
});
|
||||
|
||||
menuToPopulate.items.push({ id: 'vscode.menubar.separator' });
|
||||
}
|
||||
|
||||
if (menuToPopulate.items.length > 0) {
|
||||
menuToPopulate.items.pop();
|
||||
}
|
||||
}
|
||||
|
||||
private transformOpenRecentAction(action: Separator | (IAction & { uri: URI })): MenubarMenuItem {
|
||||
if (action instanceof Separator) {
|
||||
return { id: 'vscode.menubar.separator' };
|
||||
}
|
||||
|
||||
return {
|
||||
id: action.id,
|
||||
uri: action.uri,
|
||||
enabled: action.enabled,
|
||||
label: action.label
|
||||
};
|
||||
}
|
||||
|
||||
private getAdditionalKeybindings(): { [id: string]: IMenubarKeybinding } {
|
||||
const keybindings: { [id: string]: IMenubarKeybinding } = {};
|
||||
if (isMacintosh) {
|
||||
const keybinding = this.getMenubarKeybinding('workbench.action.quit');
|
||||
if (keybinding) {
|
||||
keybindings['workbench.action.quit'] = keybinding;
|
||||
}
|
||||
}
|
||||
|
||||
return keybindings;
|
||||
}
|
||||
|
||||
private getMenubarKeybinding(id: string): IMenubarKeybinding | undefined {
|
||||
const binding = this.keybindingService.lookupKeybinding(id);
|
||||
if (!binding) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// first try to resolve a native accelerator
|
||||
const electronAccelerator = binding.getElectronAccelerator();
|
||||
if (electronAccelerator) {
|
||||
return { label: electronAccelerator, userSettingsLabel: withNullAsUndefined(binding.getUserSettingsLabel()) };
|
||||
}
|
||||
|
||||
// we need this fallback to support keybindings that cannot show in electron menus (e.g. chords)
|
||||
const acceleratorLabel = binding.getLabel();
|
||||
if (acceleratorLabel) {
|
||||
return { label: acceleratorLabel, isNative: false, userSettingsLabel: withNullAsUndefined(binding.getUserSettingsLabel()) };
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user