host - reduce methods that are required

This commit is contained in:
Benjamin Pasero
2019-09-30 11:32:31 +02:00
parent 7928b7fb00
commit f8cdbe7421
40 changed files with 334 additions and 175 deletions
+1 -1
View File
@@ -1626,7 +1626,7 @@ export class WindowsManager extends Disposable implements IWindowsMainService {
cli = { ...cli, remote };
}
const forceReuseWindow = options && options.reuse;
const forceReuseWindow = options && options.forceReuseWindow;
const forceNewWindow = !forceReuseWindow;
return this.open({ context, cli, forceEmpty: true, forceNewWindow, forceReuseWindow });
@@ -6,7 +6,7 @@
import { Event } from 'vs/base/common/event';
import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows';
import { MessageBoxOptions, MessageBoxReturnValue, shell, OpenDevToolsOptions, SaveDialogOptions, SaveDialogReturnValue, OpenDialogOptions, OpenDialogReturnValue, CrashReporterStartOptions, crashReporter, Menu, BrowserWindow, app } from 'electron';
import { INativeOpenInWindowOptions } from 'vs/platform/windows/node/window';
import { INativeOpenWindowOptions } from 'vs/platform/windows/node/window';
import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
import { IOpenedWindow, OpenContext, IWindowOpenable, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows';
import { INativeOpenDialogOptions } from 'vs/platform/dialogs/node/dialogs';
@@ -69,11 +69,17 @@ export class ElectronMainService implements AddFirstParameterToFunctions<IElectr
return undefined;
}
async openEmptyWindow(windowId: number, options?: IOpenEmptyWindowOptions): Promise<void> {
this.windowsMainService.openEmptyWindow(OpenContext.API, options);
openWindow(windowId: number, options?: IOpenEmptyWindowOptions): Promise<void>;
openWindow(windowId: number, toOpen: IWindowOpenable[], options?: INativeOpenWindowOptions): Promise<void>;
openWindow(windowId: number, arg1?: IOpenEmptyWindowOptions | IWindowOpenable[], arg2?: INativeOpenWindowOptions): Promise<void> {
if (Array.isArray(arg1)) {
return this.doOpenWindow(windowId, arg1, arg2);
}
return this.doOpenEmptyWindow(windowId, arg1);
}
async openInWindow(windowId: number, toOpen: IWindowOpenable[], options: INativeOpenInWindowOptions = Object.create(null)): Promise<void> {
private async doOpenWindow(windowId: number, toOpen: IWindowOpenable[], options: INativeOpenWindowOptions = Object.create(null)): Promise<void> {
if (toOpen.length > 0) {
this.windowsMainService.open({
context: OpenContext.API,
@@ -91,6 +97,10 @@ export class ElectronMainService implements AddFirstParameterToFunctions<IElectr
}
}
private async doOpenEmptyWindow(windowId: number, options?: IOpenEmptyWindowOptions): Promise<void> {
this.windowsMainService.openEmptyWindow(OpenContext.API, options);
}
async toggleFullScreen(windowId: number): Promise<void> {
const window = this.windowsMainService.getWindowById(windowId);
if (window) {
@@ -267,7 +277,7 @@ export class ElectronMainService implements AddFirstParameterToFunctions<IElectr
}
}
async closeWorkpsace(windowId: number): Promise<void> {
async closeWorkspace(windowId: number): Promise<void> {
const window = this.windowsMainService.getWindowById(windowId);
if (window) {
return this.windowsMainService.closeWorkspace(window);
+4 -4
View File
@@ -11,7 +11,7 @@ import { INativeOpenDialogOptions } from 'vs/platform/dialogs/node/dialogs';
import { ISerializableCommandAction } from 'vs/platform/actions/common/actions';
import { ParsedArgs } from 'vscode-minimist';
import { IProcessEnvironment } from 'vs/base/common/platform';
import { INativeOpenInWindowOptions } from 'vs/platform/windows/node/window';
import { INativeOpenWindowOptions } from 'vs/platform/windows/node/window';
export const IElectronService = createDecorator<IElectronService>('electronService');
@@ -33,8 +33,8 @@ export interface IElectronService {
getWindowCount(): Promise<number>;
getActiveWindowId(): Promise<number | undefined>;
openEmptyWindow(options?: IOpenEmptyWindowOptions): Promise<void>;
openInWindow(toOpen: IWindowOpenable[], options?: INativeOpenInWindowOptions): Promise<void>;
openWindow(options?: IOpenEmptyWindowOptions): Promise<void>;
openWindow(toOpen: IWindowOpenable[], options?: INativeOpenWindowOptions): Promise<void>;
toggleFullScreen(): Promise<void>;
@@ -76,7 +76,7 @@ export interface IElectronService {
// Lifecycle
relaunch(options?: { addArgs?: string[], removeArgs?: string[] }): Promise<void>;
reload(): Promise<void>;
closeWorkpsace(): Promise<void>;
closeWorkspace(): Promise<void>;
closeWindow(): Promise<void>;
quit(): Promise<void>;
+6 -4
View File
@@ -19,15 +19,17 @@ export interface IOpenedWindow {
filename?: string;
}
export interface IOpenInWindowOptions {
forceNewWindow?: boolean;
export interface IBaseOpenWindowsOptions {
forceReuseWindow?: boolean;
}
export interface IOpenWindowOptions extends IBaseOpenWindowsOptions {
forceNewWindow?: boolean;
noRecentEntry?: boolean;
}
export interface IOpenEmptyWindowOptions {
reuse?: boolean;
export interface IOpenEmptyWindowOptions extends IBaseOpenWindowsOptions {
remoteAuthority?: string;
}
+2 -2
View File
@@ -3,10 +3,10 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IOpenInWindowOptions } from 'vs/platform/windows/common/windows';
import { IOpenWindowOptions } from 'vs/platform/windows/common/windows';
import { URI } from 'vs/base/common/uri';
export interface INativeOpenInWindowOptions extends IOpenInWindowOptions {
export interface INativeOpenWindowOptions extends IOpenWindowOptions {
diffMode?: boolean;
addMode?: boolean;
gotoLineMode?: boolean;
+7 -5
View File
@@ -11,7 +11,7 @@ import { ITextEditorOptions } from 'vs/platform/editor/common/editor';
import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor';
import { EditorGroupLayout } from 'vs/workbench/services/editor/common/editorGroupsService';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { IOpenInWindowOptions, IWindowOpenable } from 'vs/platform/windows/common/windows';
import { IOpenWindowOptions, IWindowOpenable, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows';
import { IWorkspacesService, hasWorkspaceFileExtension, IRecent } from 'vs/platform/workspaces/common/workspaces';
import { Schemas } from 'vs/base/common/network';
@@ -49,7 +49,7 @@ export class OpenFolderAPICommand {
if (!uri) {
return executor.executeCommand('_files.pickFolderAndOpen', { forceNewWindow: arg.forceNewWindow });
}
const options: IOpenInWindowOptions = { forceNewWindow: arg.forceNewWindow, forceReuseWindow: arg.forceReuseWindow, noRecentEntry: arg.noRecentEntry };
const options: IOpenWindowOptions = { forceNewWindow: arg.forceNewWindow, forceReuseWindow: arg.forceReuseWindow, noRecentEntry: arg.noRecentEntry };
uri = URI.revive(uri);
const uriToOpen: IWindowOpenable = (hasWorkspaceFileExtension(uri) || uri.scheme === Schemas.untitled) ? { workspaceUri: uri } : { folderUri: uri };
return executor.executeCommand('_files.windowOpen', [uriToOpen], options);
@@ -75,10 +75,12 @@ interface INewWindowAPICommandOptions {
export class NewWindowAPICommand {
public static ID = 'vscode.newWindow';
public static execute(executor: ICommandsExecutor, options?: INewWindowAPICommandOptions): Promise<any> {
return executor.executeCommand('_files.newWindow', {
reuse: options && options.reuseWindow,
const commandOptions: IOpenEmptyWindowOptions = {
forceReuseWindow: options && options.reuseWindow,
remoteAuthority: options && options.remoteAuthority
});
};
return executor.executeCommand('_files.newWindow', commandOptions);
}
}
CommandsRegistry.registerCommand({
@@ -10,7 +10,7 @@ import { IExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
import { IWindowOpenable } from 'vs/platform/windows/common/windows';
import { URI } from 'vs/base/common/uri';
import { hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces';
import { INativeOpenInWindowOptions } from 'vs/platform/windows/node/window';
import { INativeOpenWindowOptions } from 'vs/platform/windows/node/window';
export interface OpenCommandPipeArgs {
type: 'open';
@@ -127,7 +127,7 @@ export class CLIServer {
}
if (urisToOpen.length) {
const waitMarkerFileURI = waitMarkerFilePath ? URI.file(waitMarkerFilePath) : undefined;
const windowOpenArgs: INativeOpenInWindowOptions = { forceNewWindow, diffMode, addMode, gotoLineMode, forceReuseWindow, waitMarkerFileURI };
const windowOpenArgs: INativeOpenWindowOptions = { forceNewWindow, diffMode, addMode, gotoLineMode, forceReuseWindow, waitMarkerFileURI };
this._commands.executeCommand('_files.windowOpen', urisToOpen, windowOpenArgs);
}
res.writeHead(200);
@@ -135,7 +135,7 @@ abstract class BaseOpenRecentAction extends Action {
});
if (pick) {
return this.hostService.openInWindow([pick.openable], { forceNewWindow: keyMods && keyMods.ctrlCmd });
return this.hostService.openWindow([pick.openable], { forceNewWindow: keyMods && keyMods.ctrlCmd });
}
}
}
@@ -260,7 +260,7 @@ export class NewWindowAction extends Action {
}
run(): Promise<void> {
return this.hostService.openEmptyWindow();
return this.hostService.openWindow();
}
}
+1 -1
View File
@@ -297,7 +297,7 @@ export class ResourcesDropHandler {
// Open in separate windows if we drop workspaces or just one folder
if (toOpen.length > folderURIs.length || folderURIs.length === 1) {
await this.hostService.openInWindow(toOpen, { forceReuseWindow: true });
await this.hostService.openWindow(toOpen, { forceReuseWindow: true });
}
// folders.length > 1: Multiple folders: Create new workspace with folders and open
@@ -163,7 +163,7 @@ export class OpenWorkspaceButtonContribution extends Disposable implements IEdit
this._register(this.openWorkspaceButton.onClick(() => {
const model = this.editor.getModel();
if (model) {
this.hostService.openInWindow([{ workspaceUri: model.uri }]);
this.hostService.openWindow([{ workspaceUri: model.uri }]);
}
}));
@@ -222,7 +222,7 @@ export abstract class MenubarControl extends Disposable {
const ret: IAction = new Action(commandId, unmnemonicLabel(label), undefined, undefined, (event) => {
const openInNewWindow = event && ((!isMacintosh && (event.ctrlKey || event.shiftKey)) || (isMacintosh && (event.metaKey || event.altKey)));
return this.hostService.openInWindow([openable], {
return this.hostService.openWindow([openable], {
forceNewWindow: openInNewWindow
});
});
@@ -20,7 +20,7 @@ import { ExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/brow
import {
OpenExtensionsViewletAction, InstallExtensionsAction, ShowOutdatedExtensionsAction, ShowRecommendedExtensionsAction, ShowRecommendedKeymapExtensionsAction, ShowPopularExtensionsAction,
ShowEnabledExtensionsAction, ShowInstalledExtensionsAction, ShowDisabledExtensionsAction, ShowBuiltInExtensionsAction, UpdateAllAction,
EnableAllAction, EnableAllWorkpsaceAction, DisableAllAction, DisableAllWorkpsaceAction, CheckForUpdatesAction, ShowLanguageExtensionsAction, ShowAzureExtensionsAction, EnableAutoUpdateAction, DisableAutoUpdateAction, ConfigureRecommendedExtensionsCommandsContributor, InstallVSIXAction, ReinstallAction, InstallSpecificVersionOfExtensionAction
EnableAllAction, EnableAllWorkspaceAction, DisableAllAction, DisableAllWorkspaceAction, CheckForUpdatesAction, ShowLanguageExtensionsAction, ShowAzureExtensionsAction, EnableAutoUpdateAction, DisableAutoUpdateAction, ConfigureRecommendedExtensionsCommandsContributor, InstallVSIXAction, ReinstallAction, InstallSpecificVersionOfExtensionAction
} from 'vs/workbench/contrib/extensions/browser/extensionsActions';
import { ExtensionsInput } from 'vs/workbench/contrib/extensions/common/extensionsInput';
import { ViewletRegistry, Extensions as ViewletExtensions, ViewletDescriptor } from 'vs/workbench/browser/viewlet';
@@ -136,13 +136,13 @@ actionRegistry.registerWorkbenchAction(installVSIXActionDescriptor, 'Extensions:
const disableAllAction = new SyncActionDescriptor(DisableAllAction, DisableAllAction.ID, DisableAllAction.LABEL);
actionRegistry.registerWorkbenchAction(disableAllAction, 'Extensions: Disable All Installed Extensions', ExtensionsLabel);
const disableAllWorkspaceAction = new SyncActionDescriptor(DisableAllWorkpsaceAction, DisableAllWorkpsaceAction.ID, DisableAllWorkpsaceAction.LABEL);
const disableAllWorkspaceAction = new SyncActionDescriptor(DisableAllWorkspaceAction, DisableAllWorkspaceAction.ID, DisableAllWorkspaceAction.LABEL);
actionRegistry.registerWorkbenchAction(disableAllWorkspaceAction, 'Extensions: Disable All Installed Extensions for this Workspace', ExtensionsLabel);
const enableAllAction = new SyncActionDescriptor(EnableAllAction, EnableAllAction.ID, EnableAllAction.LABEL);
actionRegistry.registerWorkbenchAction(enableAllAction, 'Extensions: Enable All Extensions', ExtensionsLabel);
const enableAllWorkspaceAction = new SyncActionDescriptor(EnableAllWorkpsaceAction, EnableAllWorkpsaceAction.ID, EnableAllWorkpsaceAction.LABEL);
const enableAllWorkspaceAction = new SyncActionDescriptor(EnableAllWorkspaceAction, EnableAllWorkspaceAction.ID, EnableAllWorkspaceAction.LABEL);
actionRegistry.registerWorkbenchAction(enableAllWorkspaceAction, 'Extensions: Enable All Extensions for this Workspace', ExtensionsLabel);
const checkForUpdatesAction = new SyncActionDescriptor(CheckForUpdatesAction, CheckForUpdatesAction.ID, CheckForUpdatesAction.LABEL);
@@ -2684,14 +2684,14 @@ export class DisableAllAction extends Action {
}
}
export class DisableAllWorkpsaceAction extends Action {
export class DisableAllWorkspaceAction extends Action {
static readonly ID = 'workbench.extensions.action.disableAllWorkspace';
static LABEL = localize('disableAllWorkspace', "Disable All Installed Extensions for this Workspace");
constructor(
id: string = DisableAllWorkpsaceAction.ID, label: string = DisableAllWorkpsaceAction.LABEL,
id: string = DisableAllWorkspaceAction.ID, label: string = DisableAllWorkspaceAction.LABEL,
@IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService,
@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
@IExtensionEnablementService private readonly extensionEnablementService: IExtensionEnablementService
@@ -2736,14 +2736,14 @@ export class EnableAllAction extends Action {
}
}
export class EnableAllWorkpsaceAction extends Action {
export class EnableAllWorkspaceAction extends Action {
static readonly ID = 'workbench.extensions.action.enableAllWorkspace';
static LABEL = localize('enableAllWorkspace', "Enable All Extensions for this Workspace");
constructor(
id: string = EnableAllWorkpsaceAction.ID, label: string = EnableAllWorkpsaceAction.LABEL,
id: string = EnableAllWorkspaceAction.ID, label: string = EnableAllWorkspaceAction.LABEL,
@IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService,
@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
@IExtensionEnablementService private readonly extensionEnablementService: IExtensionEnablementService
@@ -619,28 +619,28 @@ export class ExtensionsListView extends ViewletPanel {
}
// Given all recommendations, trims and returns recommendations in the relevant order after filtering out installed extensions
private getTrimmedRecommendations(installedExtensions: IExtension[], value: string, fileBasedRecommendations: IExtensionRecommendation[], otherRecommendations: IExtensionRecommendation[], workpsaceRecommendations: IExtensionRecommendation[]): string[] {
private getTrimmedRecommendations(installedExtensions: IExtension[], value: string, fileBasedRecommendations: IExtensionRecommendation[], otherRecommendations: IExtensionRecommendation[], workspaceRecommendations: IExtensionRecommendation[]): string[] {
const totalCount = 8;
workpsaceRecommendations = workpsaceRecommendations
workspaceRecommendations = workspaceRecommendations
.filter(recommendation => {
return !this.isRecommendationInstalled(recommendation, installedExtensions)
&& recommendation.extensionId.toLowerCase().indexOf(value) > -1;
});
fileBasedRecommendations = fileBasedRecommendations.filter(recommendation => {
return !this.isRecommendationInstalled(recommendation, installedExtensions)
&& workpsaceRecommendations.every(workspaceRecommendation => workspaceRecommendation.extensionId !== recommendation.extensionId)
&& workspaceRecommendations.every(workspaceRecommendation => workspaceRecommendation.extensionId !== recommendation.extensionId)
&& recommendation.extensionId.toLowerCase().indexOf(value) > -1;
});
otherRecommendations = otherRecommendations.filter(recommendation => {
return !this.isRecommendationInstalled(recommendation, installedExtensions)
&& fileBasedRecommendations.every(fileBasedRecommendation => fileBasedRecommendation.extensionId !== recommendation.extensionId)
&& workpsaceRecommendations.every(workspaceRecommendation => workspaceRecommendation.extensionId !== recommendation.extensionId)
&& workspaceRecommendations.every(workspaceRecommendation => workspaceRecommendation.extensionId !== recommendation.extensionId)
&& recommendation.extensionId.toLowerCase().indexOf(value) > -1;
});
const otherCount = Math.min(2, otherRecommendations.length);
const fileBasedCount = Math.min(fileBasedRecommendations.length, totalCount - workpsaceRecommendations.length - otherCount);
const recommendations = workpsaceRecommendations;
const fileBasedCount = Math.min(fileBasedRecommendations.length, totalCount - workspaceRecommendations.length - otherCount);
const recommendations = workspaceRecommendations;
recommendations.push(...fileBasedRecommendations.splice(0, fileBasedCount));
recommendations.push(...otherRecommendations.splice(0, otherCount));
@@ -737,7 +737,7 @@ export class ShowOpenedFileInNewWindow extends Action {
const fileResource = toResource(this.editorService.activeEditor, { supportSideBySide: SideBySideEditor.MASTER });
if (fileResource) {
if (this.fileService.canHandleResource(fileResource)) {
this.hostService.openInWindow([{ fileUri: fileResource }], { forceNewWindow: true });
this.hostService.openWindow([{ fileUri: fileResource }], { forceNewWindow: true });
} else {
this.notificationService.info(nls.localize('openFileToShowInNewWindow.unsupportedschema', "The active editor must contain an openable resource."));
}
@@ -6,7 +6,7 @@
import * as nls from 'vs/nls';
import { URI } from 'vs/base/common/uri';
import { toResource, IEditorCommandsContext, SideBySideEditor } from 'vs/workbench/common/editor';
import { IWindowOpenable, IOpenInWindowOptions, isWorkspaceToOpen, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows';
import { IWindowOpenable, IOpenWindowOptions, isWorkspaceToOpen, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows';
import { IHostService } from 'vs/workbench/services/host/browser/host';
import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
@@ -78,7 +78,7 @@ export const ResourceSelectedForCompareContext = new RawContextKey<boolean>('res
export const REMOVE_ROOT_FOLDER_COMMAND_ID = 'removeRootFolder';
export const REMOVE_ROOT_FOLDER_LABEL = nls.localize('removeFolderFromWorkspace', "Remove Folder from Workspace");
export const openWindowCommand = (accessor: ServicesAccessor, toOpen: IWindowOpenable[], options?: IOpenInWindowOptions) => {
export const openWindowCommand = (accessor: ServicesAccessor, toOpen: IWindowOpenable[], options?: IOpenWindowOptions) => {
if (Array.isArray(toOpen)) {
const hostService = accessor.get(IHostService);
const environmentService = accessor.get(IEnvironmentService);
@@ -94,13 +94,13 @@ export const openWindowCommand = (accessor: ServicesAccessor, toOpen: IWindowOpe
return openable;
});
hostService.openInWindow(toOpen, options);
hostService.openWindow(toOpen, options);
}
};
export const newWindowCommand = (accessor: ServicesAccessor, options?: IOpenEmptyWindowOptions) => {
const hostService = accessor.get(IHostService);
hostService.openEmptyWindow(options);
hostService.openWindow(options);
};
async function save(
@@ -21,7 +21,6 @@ import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
import { didUseCachedData, ITimerService } from 'vs/workbench/services/timer/electron-browser/timerService';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { getEntries } from 'vs/base/common/performance';
import { IHostService } from 'vs/workbench/services/host/browser/host';
export class StartupTimings implements IWorkbenchContribution {
@@ -34,8 +33,7 @@ export class StartupTimings implements IWorkbenchContribution {
@ITelemetryService private readonly _telemetryService: ITelemetryService,
@ILifecycleService private readonly _lifecycleService: ILifecycleService,
@IUpdateService private readonly _updateService: IUpdateService,
@IEnvironmentService private readonly _envService: IEnvironmentService,
@IHostService private readonly _hostService: IHostService
@IEnvironmentService private readonly _envService: IEnvironmentService
) {
//
this._report().catch(onUnexpectedError);
@@ -93,7 +91,7 @@ export class StartupTimings implements IWorkbenchContribution {
if (this._lifecycleService.startupKind !== StartupKind.NewWindow) {
return false;
}
if (await this._hostService.windowCount !== 1) {
if (await this._electronService.getWindowCount() !== 1) {
return false;
}
const activeViewlet = this._viewletService.getActiveViewlet();
@@ -374,7 +374,7 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon
constructor(
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,
@IPreferencesService private readonly preferencesService: IPreferencesService,
@IWorkspaceContextService private readonly workpsaceContextService: IWorkspaceContextService,
@IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService,
@ILabelService labelService: ILabelService,
@IExtensionService extensionService: IExtensionService,
) {
@@ -410,8 +410,8 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon
});
this.updatePreferencesEditorMenuItem();
this._register(workpsaceContextService.onDidChangeWorkbenchState(() => this.updatePreferencesEditorMenuItem()));
this._register(workpsaceContextService.onDidChangeWorkspaceFolders(() => this.updatePreferencesEditorMenuItemForWorkspaceFolders()));
this._register(workspaceContextService.onDidChangeWorkbenchState(() => this.updatePreferencesEditorMenuItem()));
this._register(workspaceContextService.onDidChangeWorkspaceFolders(() => this.updatePreferencesEditorMenuItemForWorkspaceFolders()));
extensionService.whenInstalledExtensionsRegistered()
.then(() => {
@@ -434,7 +434,7 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon
private updatePreferencesEditorMenuItem() {
const commandId = '_workbench.openWorkspaceSettingsEditor';
if (this.workpsaceContextService.getWorkbenchState() === WorkbenchState.WORKSPACE && !CommandsRegistry.getCommand(commandId)) {
if (this.workspaceContextService.getWorkbenchState() === WorkbenchState.WORKSPACE && !CommandsRegistry.getCommand(commandId)) {
CommandsRegistry.registerCommand(commandId, () => this.preferencesService.openWorkspaceSettings(false));
MenuRegistry.appendMenuItem(MenuId.EditorTitle, {
command: {
@@ -454,11 +454,11 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon
}
private updatePreferencesEditorMenuItemForWorkspaceFolders() {
for (const folder of this.workpsaceContextService.getWorkspace().folders) {
for (const folder of this.workspaceContextService.getWorkspace().folders) {
const commandId = `_workbench.openFolderSettings.${folder.uri.toString()}`;
if (!CommandsRegistry.getCommand(commandId)) {
CommandsRegistry.registerCommand(commandId, () => {
if (this.workpsaceContextService.getWorkbenchState() === WorkbenchState.FOLDER) {
if (this.workspaceContextService.getWorkbenchState() === WorkbenchState.FOLDER) {
return this.preferencesService.openWorkspaceSettings(false);
} else {
return this.preferencesService.openFolderSettings(folder.uri, false);
@@ -91,7 +91,7 @@ export class RemoteWindowActiveIndicator extends Disposable implements IWorkbenc
menu: {
menuId: MenuId.CommandPalette
},
handler: (_accessor) => this.remoteAuthority && hostService.openEmptyWindow({ reuse: true })
handler: (_accessor) => this.remoteAuthority && hostService.openWindow({ forceReuseWindow: true })
});
// Pending entry until extensions are ready
@@ -454,7 +454,7 @@ export class WorkspaceStatsService implements IWorkspaceStatsService {
this.notificationService.prompt(Severity.Info, localize('workspaceFound', "This folder contains a workspace file '{0}'. Do you want to open it? [Learn more]({1}) about workspace files.", workspaceFile, 'https://go.microsoft.com/fwlink/?linkid=2025315'), [{
label: localize('openWorkspace', "Open Workspace"),
run: () => this.hostService.openInWindow([{ workspaceUri: joinPath(folder, workspaceFile) }])
run: () => this.hostService.openWindow([{ workspaceUri: joinPath(folder, workspaceFile) }])
}], { neverShowAgain });
}
@@ -467,7 +467,7 @@ export class WorkspaceStatsService implements IWorkspaceStatsService {
workspaces.map(workspace => ({ label: workspace } as IQuickPickItem)),
{ placeHolder: localize('selectToOpen', "Select a workspace to open") }).then(pick => {
if (pick) {
this.hostService.openInWindow([{ workspaceUri: joinPath(folder, pick.label) }]);
this.hostService.openWindow([{ workspaceUri: joinPath(folder, pick.label) }]);
}
});
}
@@ -4,10 +4,8 @@
*--------------------------------------------------------------------------------------------*/
import { Registry } from 'vs/platform/registry/common/platform';
import { TelemetryOptOut } from './telemetryOptOut';
import { BrowserTelemetryOptOut } from 'vs/workbench/contrib/welcome/gettingStarted/browser/telemetryOptOut';
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
Registry
.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench)
.registerWorkbenchContribution(TelemetryOptOut, LifecyclePhase.Eventually);
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(BrowserTelemetryOptOut, LifecyclePhase.Eventually);
@@ -19,7 +19,7 @@ import { CancellationToken } from 'vs/base/common/cancellation';
import { IProductService } from 'vs/platform/product/common/productService';
import { IHostService } from 'vs/workbench/services/host/browser/host';
export class TelemetryOptOut implements IWorkbenchContribution {
export abstract class AbstractTelemetryOptOut implements IWorkbenchContribution {
private static TELEMETRY_OPT_OUT_SHOWN = 'workbench.telemetryOptOutShown';
private privacyUrl: string | undefined;
@@ -35,18 +35,18 @@ export class TelemetryOptOut implements IWorkbenchContribution {
@IExtensionGalleryService private readonly galleryService: IExtensionGalleryService,
@IProductService productService: IProductService
) {
if (!productService.telemetryOptOutUrl || storageService.get(TelemetryOptOut.TELEMETRY_OPT_OUT_SHOWN, StorageScope.GLOBAL)) {
if (!productService.telemetryOptOutUrl || storageService.get(AbstractTelemetryOptOut.TELEMETRY_OPT_OUT_SHOWN, StorageScope.GLOBAL)) {
return;
}
const experimentId = 'telemetryOptOut';
Promise.all([
hostService.windowCount,
this.getWindowCount(),
experimentService.getExperimentById(experimentId)
]).then(([count, experimentState]) => {
if (!hostService.hasFocus && count > 1) {
return;
}
storageService.store(TelemetryOptOut.TELEMETRY_OPT_OUT_SHOWN, true, StorageScope.GLOBAL);
storageService.store(AbstractTelemetryOptOut.TELEMETRY_OPT_OUT_SHOWN, true, StorageScope.GLOBAL);
this.privacyUrl = productService.privacyStatementUrl || productService.telemetryOptOutUrl;
@@ -74,6 +74,8 @@ export class TelemetryOptOut implements IWorkbenchContribution {
.then(undefined, onUnexpectedError);
}
protected abstract getWindowCount(): Promise<number>;
private runExperiment(experimentId: string) {
const promptMessageKey = 'telemetryOptOut.optOutOption';
const yesLabelKey = 'telemetryOptOut.OptIn';
@@ -148,3 +150,10 @@ export class TelemetryOptOut implements IWorkbenchContribution {
});
}
}
export class BrowserTelemetryOptOut extends AbstractTelemetryOptOut {
protected async getWindowCount(): Promise<number> {
return 1;
}
}
@@ -4,10 +4,10 @@
*--------------------------------------------------------------------------------------------*/
import { Registry } from 'vs/platform/registry/common/platform';
import { OpenWelcomePageInBrowser } from './openWebsite';
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { NativeTelemetryOptOut } from 'vs/workbench/contrib/welcome/gettingStarted/electron-browser/telemetryOptOut';
import { OpenWelcomePageInBrowser } from 'vs/workbench/contrib/welcome/gettingStarted/electron-browser/openWebsite';
Registry
.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench)
.registerWorkbenchContribution(OpenWelcomePageInBrowser, LifecyclePhase.Restored);
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(OpenWelcomePageInBrowser, LifecyclePhase.Restored);
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(NativeTelemetryOptOut, LifecyclePhase.Eventually);
@@ -0,0 +1,38 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IStorageService } from 'vs/platform/storage/common/storage';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IExperimentService } from 'vs/workbench/contrib/experiments/common/experimentService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IProductService } from 'vs/platform/product/common/productService';
import { IHostService } from 'vs/workbench/services/host/browser/host';
import { AbstractTelemetryOptOut } from 'vs/workbench/contrib/welcome/gettingStarted/browser/telemetryOptOut';
import { IElectronService } from 'vs/platform/electron/node/electron';
export class NativeTelemetryOptOut extends AbstractTelemetryOptOut {
constructor(
@IStorageService storageService: IStorageService,
@IOpenerService openerService: IOpenerService,
@INotificationService notificationService: INotificationService,
@IHostService hostService: IHostService,
@ITelemetryService telemetryService: ITelemetryService,
@IExperimentService experimentService: IExperimentService,
@IConfigurationService configurationService: IConfigurationService,
@IExtensionGalleryService galleryService: IExtensionGalleryService,
@IProductService productService: IProductService,
@IElectronService private readonly electronService: IElectronService
) {
super(storageService, openerService, notificationService, hostService, telemetryService, experimentService, configurationService, galleryService, productService);
}
protected getWindowCount(): Promise<number> {
return this.electronService.getWindowCount();
}
}
@@ -365,7 +365,7 @@ class WelcomePage extends Disposable {
id: 'openRecentFolder',
from: telemetryFrom
});
this.hostService.openInWindow([windowOpenable], { forceNewWindow: e.ctrlKey || e.metaKey });
this.hostService.openWindow([windowOpenable], { forceNewWindow: e.ctrlKey || e.metaKey });
e.preventDefault();
e.stopPropagation();
});
@@ -65,6 +65,6 @@ export class DuplicateWorkspaceInNewWindowAction extends Action {
const newWorkspace = await this.workspacesService.createUntitledWorkspace(folders, remoteAuthority);
await this.workspaceEditingService.copyWorkspaceSettings(newWorkspace);
return this.hostService.openInWindow([{ workspaceUri: newWorkspace.configPath }], { forceNewWindow: true });
return this.hostService.openWindow([{ workspaceUri: newWorkspace.configPath }], { forceNewWindow: true });
}
}
@@ -91,7 +91,7 @@ export abstract class AbstractFileDialogService {
const toOpen: IWindowOpenable = stat.isDirectory ? { folderUri: uri } : { fileUri: uri };
if (stat.isDirectory || options.forceNewWindow || preferNewWindow) {
return this.hostService.openInWindow([toOpen], { forceNewWindow: options.forceNewWindow });
return this.hostService.openWindow([toOpen], { forceNewWindow: options.forceNewWindow });
} else {
return this.openerService.open(uri);
}
@@ -105,7 +105,7 @@ export abstract class AbstractFileDialogService {
const uri = await this.pickResource({ canSelectFiles: true, canSelectFolders: false, canSelectMany: false, defaultUri: options.defaultUri, title, availableFileSystems });
if (uri) {
if (options.forceNewWindow || preferNewWindow) {
return this.hostService.openInWindow([{ fileUri: uri }], { forceNewWindow: options.forceNewWindow });
return this.hostService.openWindow([{ fileUri: uri }], { forceNewWindow: options.forceNewWindow });
} else {
return this.openerService.open(uri);
}
@@ -118,7 +118,7 @@ export abstract class AbstractFileDialogService {
const uri = await this.pickResource({ canSelectFiles: false, canSelectFolders: true, canSelectMany: false, defaultUri: options.defaultUri, title, availableFileSystems });
if (uri) {
return this.hostService.openInWindow([{ folderUri: uri }], { forceNewWindow: options.forceNewWindow });
return this.hostService.openWindow([{ folderUri: uri }], { forceNewWindow: options.forceNewWindow });
}
}
@@ -129,7 +129,7 @@ export abstract class AbstractFileDialogService {
const uri = await this.pickResource({ canSelectFiles: true, canSelectFolders: false, canSelectMany: false, defaultUri: options.defaultUri, title, filters, availableFileSystems });
if (uri) {
return this.hostService.openInWindow([{ workspaceUri: uri }], { forceNewWindow: options.forceNewWindow });
return this.hostService.openWindow([{ workspaceUri: uri }], { forceNewWindow: options.forceNewWindow });
}
}
@@ -9,7 +9,7 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { IResourceEditor, IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IWindowSettings, IWindowOpenable, IOpenInWindowOptions, isFolderToOpen, isWorkspaceToOpen, isFileToOpen, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows';
import { IWindowSettings, IWindowOpenable, IOpenWindowOptions, isFolderToOpen, isWorkspaceToOpen, isFileToOpen, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows';
import { pathsToEditors } from 'vs/workbench/common/editor';
import { IFileService } from 'vs/platform/files/common/files';
import { ILabelService } from 'vs/platform/label/common/label';
@@ -46,13 +46,6 @@ export class BrowserHostService extends Disposable implements IHostService {
_serviceBrand: undefined;
//#region Events
get onDidChangeFocus(): Event<boolean> { return this._onDidChangeFocus; }
private _onDidChangeFocus: Event<boolean>;
//#endregion
private workspaceProvider: IWorkspaceProvider;
constructor(
@@ -87,11 +80,28 @@ export class BrowserHostService extends Disposable implements IHostService {
);
}
//#region Window
get onDidChangeFocus(): Event<boolean> { return this._onDidChangeFocus; }
private _onDidChangeFocus: Event<boolean>;
readonly windowCount = Promise.resolve(1);
get hasFocus(): boolean {
return document.hasFocus();
}
async openInWindow(toOpen: IWindowOpenable[], options?: IOpenInWindowOptions): Promise<void> {
async focus(): Promise<void> {
window.focus();
}
openWindow(options?: IOpenEmptyWindowOptions): Promise<void>;
openWindow(toOpen: IWindowOpenable[], options?: IOpenWindowOptions): Promise<void>;
openWindow(arg1?: IOpenEmptyWindowOptions | IWindowOpenable[], arg2?: IOpenWindowOptions): Promise<void> {
if (Array.isArray(arg1)) {
return this.doOpenWindow(arg1, arg2);
}
return this.doOpenEmptyWindow(arg1);
}
private async doOpenWindow(toOpen: IWindowOpenable[], options?: IOpenWindowOptions): Promise<void> {
for (let i = 0; i < toOpen.length; i++) {
const openable = toOpen[i];
openable.label = openable.label || this.getRecentLabel(openable);
@@ -126,7 +136,7 @@ export class BrowserHostService extends Disposable implements IHostService {
return this.labelService.getUriLabel(openable.fileUri);
}
private shouldReuse(options: IOpenInWindowOptions = {}): boolean {
private shouldReuse(options: IOpenWindowOptions = {}): boolean {
const windowConfig = this.configurationService.getValue<IWindowSettings>('window');
const openFolderInNewWindowConfig = (windowConfig && windowConfig.openFoldersInNewWindow) || 'default' /* default */;
@@ -138,8 +148,8 @@ export class BrowserHostService extends Disposable implements IHostService {
return !openFolderInNewWindow;
}
async openEmptyWindow(options?: IOpenEmptyWindowOptions): Promise<void> {
this.workspaceProvider.open(undefined, { reuse: options && options.reuse });
private async doOpenEmptyWindow(options?: IOpenEmptyWindowOptions): Promise<void> {
this.workspaceProvider.open(undefined, { reuse: options && options.forceReuseWindow });
}
async toggleFullScreen(): Promise<void> {
@@ -176,16 +186,6 @@ export class BrowserHostService extends Disposable implements IHostService {
}
}
get hasFocus(): boolean {
return document.hasFocus();
}
async focus(): Promise<void> {
window.focus();
}
//#endregion
async restart(): Promise<void> {
this.reload();
}
@@ -195,7 +195,7 @@ export class BrowserHostService extends Disposable implements IHostService {
}
async closeWorkspace(): Promise<void> {
return this.openEmptyWindow({ reuse: true });
return this.doOpenEmptyWindow({ forceReuseWindow: true });
}
}
+24 -27
View File
@@ -5,7 +5,7 @@
import { Event } from 'vs/base/common/event';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IWindowOpenable, IOpenInWindowOptions, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows';
import { IWindowOpenable, IOpenWindowOptions, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows';
export const IHostService = createDecorator<IHostService>('hostService');
@@ -13,38 +13,13 @@ export interface IHostService {
_serviceBrand: undefined;
//#region Events
//#region Focus
/**
* Emitted when the window focus changes.
*/
readonly onDidChangeFocus: Event<boolean>;
//#endregion
//#region Window
/**
* The number of windows that belong to the current client session.
*/
readonly windowCount: Promise<number>;
/**
* Opens the provided array of openables in a window with the provided options.
*/
openInWindow(toOpen: IWindowOpenable[], options?: IOpenInWindowOptions): Promise<void>;
/**
* Opens an empty window. The optional parameter allows to define if
* a new window should open or the existing one change to an empty.
*/
openEmptyWindow(options?: IOpenEmptyWindowOptions): Promise<void>;
/**
* Switch between fullscreen and normal window.
*/
toggleFullScreen(): Promise<void>;
/**
* Find out if the window has focus or not.
*/
@@ -57,6 +32,28 @@ export interface IHostService {
//#endregion
//#region Window
/**
* Opens an empty window. The optional parameter allows to define if
* a new window should open or the existing one change to an empty.
*/
openWindow(options?: IOpenEmptyWindowOptions): Promise<void>;
/**
* Opens the provided array of openables in a window with the provided options.
*/
openWindow(toOpen: IWindowOpenable[], options?: IOpenWindowOptions): Promise<void>;
/**
* Switch between fullscreen and normal window.
*/
toggleFullScreen(): Promise<void>;
//#endregion
//#region Lifecycle
/**
@@ -9,7 +9,7 @@ import { IElectronService } from 'vs/platform/electron/node/electron';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { ILabelService } from 'vs/platform/label/common/label';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { IWindowOpenable, IOpenInWindowOptions, isFolderToOpen, isWorkspaceToOpen, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows';
import { IWindowOpenable, IOpenWindowOptions, isFolderToOpen, isWorkspaceToOpen, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows';
import { Disposable } from 'vs/base/common/lifecycle';
import { IElectronEnvironmentService } from 'vs/workbench/services/electron/electron-browser/electronEnvironmentService';
@@ -17,16 +17,6 @@ export class DesktopHostService extends Disposable implements IHostService {
_serviceBrand: undefined;
//#region Events
get onDidChangeFocus(): Event<boolean> { return this._onDidChangeFocus; }
private _onDidChangeFocus: Event<boolean> = Event.any(
Event.map(Event.filter(this.electronService.onWindowFocus, id => id === this.electronEnvironmentService.windowId), _ => true),
Event.map(Event.filter(this.electronService.onWindowBlur, id => id === this.electronEnvironmentService.windowId), _ => false)
);
//#endregion
constructor(
@IElectronService private readonly electronService: IElectronService,
@ILabelService private readonly labelService: ILabelService,
@@ -46,19 +36,31 @@ export class DesktopHostService extends Disposable implements IHostService {
this._register(this.onDidChangeFocus(focus => this._hasFocus = focus));
}
//#region Window
get onDidChangeFocus(): Event<boolean> { return this._onDidChangeFocus; }
private _onDidChangeFocus: Event<boolean> = Event.any(
Event.map(Event.filter(this.electronService.onWindowFocus, id => id === this.electronEnvironmentService.windowId), _ => true),
Event.map(Event.filter(this.electronService.onWindowBlur, id => id === this.electronEnvironmentService.windowId), _ => false)
);
private _hasFocus: boolean;
get hasFocus(): boolean { return this._hasFocus; }
get windowCount(): Promise<number> { return this.electronService.getWindowCount(); }
openWindow(options?: IOpenEmptyWindowOptions): Promise<void>;
openWindow(toOpen: IWindowOpenable[], options?: IOpenWindowOptions): Promise<void>;
openWindow(arg1?: IOpenEmptyWindowOptions | IWindowOpenable[], arg2?: IOpenWindowOptions): Promise<void> {
if (Array.isArray(arg1)) {
return this.doOpenWindow(arg1, arg2);
}
openInWindow(toOpen: IWindowOpenable[], options?: IOpenInWindowOptions): Promise<void> {
return this.doOpenEmptyWindow(arg1);
}
private doOpenWindow(toOpen: IWindowOpenable[], options?: IOpenWindowOptions): Promise<void> {
if (!!this.environmentService.configuration.remoteAuthority) {
toOpen.forEach(openable => openable.label = openable.label || this.getRecentLabel(openable));
}
return this.electronService.openInWindow(toOpen, options);
return this.electronService.openWindow(toOpen, options);
}
private getRecentLabel(openable: IWindowOpenable): string {
@@ -73,8 +75,8 @@ export class DesktopHostService extends Disposable implements IHostService {
return this.labelService.getUriLabel(openable.fileUri);
}
openEmptyWindow(options?: IOpenEmptyWindowOptions): Promise<void> {
return this.electronService.openEmptyWindow(options);
private doOpenEmptyWindow(options?: IOpenEmptyWindowOptions): Promise<void> {
return this.electronService.openWindow(options);
}
toggleFullScreen(): Promise<void> {
@@ -85,8 +87,6 @@ export class DesktopHostService extends Disposable implements IHostService {
return this.electronService.focusWindow();
}
//#endregion
restart(): Promise<void> {
return this.electronService.relaunch();
}
@@ -96,7 +96,7 @@ export class DesktopHostService extends Disposable implements IHostService {
}
closeWorkspace(): Promise<void> {
return this.electronService.closeWorkpsace();
return this.electronService.closeWorkspace();
}
}
@@ -57,6 +57,10 @@ export class BrowserTextFileService extends AbstractTextFileService {
return false; // dirty with backups: no veto
}
protected async getWindowCount(): Promise<number> {
return 1; // Browser only ever is 1 window
}
}
registerSingleton(ITextFileService, BrowserTextFileService);
@@ -39,7 +39,6 @@ import { VSBuffer } from 'vs/base/common/buffer';
import { ITextSnapshot } from 'vs/editor/common/model';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry';
import { IHostService } from 'vs/workbench/services/host/browser/host';
/**
* The workbench file service implementation implements the raw file service spec and adds additional methods on top.
@@ -74,14 +73,13 @@ export abstract class AbstractTextFileService extends Disposable implements ITex
@IFileService protected readonly fileService: IFileService,
@IUntitledEditorService protected readonly untitledEditorService: IUntitledEditorService,
@ILifecycleService private readonly lifecycleService: ILifecycleService,
@IInstantiationService protected instantiationService: IInstantiationService,
@IInstantiationService protected readonly instantiationService: IInstantiationService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IModeService private readonly modeService: IModeService,
@IModelService private readonly modelService: IModelService,
@IWorkbenchEnvironmentService protected readonly environmentService: IWorkbenchEnvironmentService,
@INotificationService private readonly notificationService: INotificationService,
@IBackupFileService private readonly backupFileService: IBackupFileService,
@IHostService private readonly hostService: IHostService,
@IHistoryService private readonly historyService: IHistoryService,
@IContextKeyService contextKeyService: IContextKeyService,
@IDialogService private readonly dialogService: IDialogService,
@@ -180,7 +178,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex
case ShutdownReason.CLOSE:
if (this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY && this.configuredHotExit === HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE) {
doBackup = true; // backup if a folder is open and onExitAndWindowClose is configured
} else if (await this.hostService.windowCount > 1 || platform.isMacintosh) {
} else if (await this.getWindowCount() > 1 || platform.isMacintosh) {
doBackup = false; // do not backup if a window is closed that does not cause quitting of the application
} else {
doBackup = true; // backup if last window is closed on win/linux where the application quits right after
@@ -213,6 +211,8 @@ export abstract class AbstractTextFileService extends Disposable implements ITex
return true;
}
protected abstract getWindowCount(): Promise<number>;
private backupAll(dirtyToBackup: URI[]): Promise<void> {
// split up between files and untitled
@@ -27,9 +27,46 @@ import { Readable } from 'stream';
import { createTextBufferFactoryFromStream } from 'vs/editor/common/model/textModel';
import { ITextSnapshot } from 'vs/editor/common/model';
import { nodeReadableToString, streamToNodeReadable, nodeStreamToVSBufferReadable } from 'vs/base/node/stream';
import { IElectronService } from 'vs/platform/electron/node/electron';
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IModelService } from 'vs/editor/common/services/modelService';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
import { IHistoryService } from 'vs/workbench/services/history/common/history';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IDialogService, IFileDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
export class NativeTextFileService extends AbstractTextFileService {
constructor(
@IWorkspaceContextService contextService: IWorkspaceContextService,
@IFileService fileService: IFileService,
@IUntitledEditorService untitledEditorService: IUntitledEditorService,
@ILifecycleService lifecycleService: ILifecycleService,
@IInstantiationService instantiationService: IInstantiationService,
@IConfigurationService configurationService: IConfigurationService,
@IModeService modeService: IModeService,
@IModelService modelService: IModelService,
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,
@INotificationService notificationService: INotificationService,
@IBackupFileService backupFileService: IBackupFileService,
@IHistoryService historyService: IHistoryService,
@IContextKeyService contextKeyService: IContextKeyService,
@IDialogService dialogService: IDialogService,
@IFileDialogService fileDialogService: IFileDialogService,
@IEditorService editorService: IEditorService,
@ITextResourceConfigurationService textResourceConfigurationService: ITextResourceConfigurationService,
@IElectronService private readonly electronService: IElectronService
) {
super(contextService, fileService, untitledEditorService, lifecycleService, instantiationService, configurationService, modeService, modelService, environmentService, notificationService, backupFileService, historyService, contextKeyService, dialogService, fileDialogService, editorService, textResourceConfigurationService);
}
private _encoding!: EncodingOracle;
get encoding(): EncodingOracle {
if (!this._encoding) {
@@ -255,6 +292,10 @@ export class NativeTextFileService extends AbstractTextFileService {
});
});
}
protected getWindowCount(): Promise<number> {
return this.electronService.getWindowCount();
}
}
export interface IEncodingOverride {
@@ -7,7 +7,7 @@ import * as sinon from 'sinon';
import * as platform from 'vs/base/common/platform';
import { URI } from 'vs/base/common/uri';
import { ILifecycleService, BeforeShutdownEvent, ShutdownReason } from 'vs/platform/lifecycle/common/lifecycle';
import { workbenchInstantiationService, TestLifecycleService, TestTextFileService, TestContextService, TestFileService, TestHostService } from 'vs/workbench/test/workbenchTestServices';
import { workbenchInstantiationService, TestLifecycleService, TestTextFileService, TestContextService, TestFileService, TestElectronService } from 'vs/workbench/test/workbenchTestServices';
import { toResource } from 'vs/base/test/common/utils';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel';
@@ -20,7 +20,7 @@ import { IWorkspaceContextService, Workspace } from 'vs/platform/workspace/commo
import { IModelService } from 'vs/editor/common/services/modelService';
import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl';
import { Schemas } from 'vs/base/common/network';
import { IHostService } from 'vs/workbench/services/host/browser/host';
import { IElectronService } from 'vs/platform/electron/node/electron';
class ServiceAccessor {
constructor(
@@ -30,7 +30,7 @@ class ServiceAccessor {
@IWorkspaceContextService public contextService: TestContextService,
@IModelService public modelService: ModelServiceImpl,
@IFileService public fileService: TestFileService,
@IHostService public hostService: TestHostService
@IElectronService public electronService: TestElectronService
) {
}
}
@@ -424,7 +424,7 @@ suite('Files - TextFileService', () => {
}
// Set multiple windows if required
if (multipleWindows) {
accessor.hostService.windowCount = Promise.resolve(2);
accessor.electronService.windowCount = Promise.resolve(2);
}
// Set cancel to force a veto if hot exit does not trigger
service.setConfirmResult(ConfirmResult.CANCEL);
@@ -7,7 +7,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'
import { virtualMachineHint } from 'vs/base/node/id';
import * as perf from 'vs/base/common/performance';
import * as os from 'os';
import { IHostService } from 'vs/workbench/services/host/browser/host';
import { IElectronService } from 'vs/platform/electron/node/electron';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
@@ -308,7 +308,7 @@ class TimerService implements ITimerService {
private _startupMetrics?: Promise<IStartupMetrics>;
constructor(
@IHostService private readonly _hostService: IHostService,
@IElectronService private readonly _electronService: IElectronService,
@IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService,
@ILifecycleService private readonly _lifecycleService: ILifecycleService,
@IWorkspaceContextService private readonly _contextService: IWorkspaceContextService,
@@ -380,7 +380,7 @@ class TimerService implements ITimerService {
isLatestVersion: Boolean(await this._updateService.isLatestVersion()),
didUseCachedData: didUseCachedData(),
windowKind: this._lifecycleService.startupKind,
windowCount: await this._hostService.windowCount,
windowCount: await this._electronService.getWindowCount(),
viewletId: activeViewlet ? activeViewlet.getId() : undefined,
editorIds: this._editorService.visibleEditors.map(input => input.getTypeId()),
panelId: activePanel ? activePanel.getId() : undefined,
@@ -78,8 +78,7 @@ export class NativeWorkspaceEditingService extends AbstractWorkspaceEditingServi
return false; // only care about untitled workspaces to ask for saving
}
const windowCount = await this.hostService.windowCount;
const windowCount = await this.electronService.getWindowCount();
if (reason === ShutdownReason.CLOSE && !isMacintosh && windowCount === 1) {
return false; // Windows/Linux: quits when last window is closed, so do not ask then
}
+70 -11
View File
@@ -8,7 +8,7 @@ import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileE
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { join } from 'vs/base/common/path';
import * as resources from 'vs/base/common/resources';
import { URI } from 'vs/base/common/uri';
import { URI, UriComponents } from 'vs/base/common/uri';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
import { ConfirmResult, IEditorInputWithOptions, CloseDirection, IEditorIdentifier, IUntitledResourceInput, IResourceDiffInput, IResourceSideBySideInput, IEditorInput, IEditor, IEditorCloseEvent, IEditorPartOptions } from 'vs/workbench/common/editor';
@@ -35,7 +35,7 @@ import { IModeService } from 'vs/editor/common/services/modeService';
import { IHistoryService } from 'vs/workbench/services/history/common/history';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { MenuBarVisibility, IWindowConfiguration, IWindowOpenable, IOpenInWindowOptions, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows';
import { MenuBarVisibility, IWindowConfiguration, IWindowOpenable, IOpenWindowOptions, IOpenEmptyWindowOptions, IOpenedWindow } from 'vs/platform/windows/common/windows';
import { TestWorkspace } from 'vs/platform/workspace/test/common/testWorkspace';
import { createTextBufferFactoryFromStream } from 'vs/editor/common/model/textModel';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
@@ -70,7 +70,7 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { ViewletDescriptor, Viewlet } from 'vs/workbench/browser/viewlet';
import { IViewlet } from 'vs/workbench/common/viewlet';
import { IStorageService, InMemoryStorageService } from 'vs/platform/storage/common/storage';
import { isLinux, isMacintosh } from 'vs/base/common/platform';
import { isLinux, isMacintosh, IProcessEnvironment } from 'vs/base/common/platform';
import { LabelService } from 'vs/workbench/services/label/common/labelService';
import { IDimension } from 'vs/platform/layout/browser/layoutService';
import { Part } from 'vs/workbench/browser/part';
@@ -86,6 +86,8 @@ import { Schemas } from 'vs/base/common/network';
import { IProductService } from 'vs/platform/product/common/productService';
import product from 'vs/platform/product/common/product';
import { IHostService } from 'vs/workbench/services/host/browser/host';
import { IElectronService } from 'vs/platform/electron/node/electron';
import { INativeOpenDialogOptions } from 'vs/platform/dialogs/node/dialogs';
export function createFileInput(instantiationService: IInstantiationService, resource: URI): FileEditorInput {
return instantiationService.createInstance(FileEditorInput, resource, undefined, undefined);
@@ -199,13 +201,13 @@ export class TestTextFileService extends NativeTextFileService {
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,
@INotificationService notificationService: INotificationService,
@IBackupFileService backupFileService: IBackupFileService,
@IHostService hostService: IHostService,
@IHistoryService historyService: IHistoryService,
@IContextKeyService contextKeyService: IContextKeyService,
@IDialogService dialogService: IDialogService,
@IFileDialogService fileDialogService: IFileDialogService,
@IEditorService editorService: IEditorService,
@ITextResourceConfigurationService textResourceConfigurationService: ITextResourceConfigurationService
@ITextResourceConfigurationService textResourceConfigurationService: ITextResourceConfigurationService,
@IElectronService electronService: IElectronService
) {
super(
contextService,
@@ -219,13 +221,13 @@ export class TestTextFileService extends NativeTextFileService {
environmentService,
notificationService,
backupFileService,
hostService,
historyService,
contextKeyService,
dialogService,
fileDialogService,
editorService,
textResourceConfigurationService
textResourceConfigurationService,
electronService
);
}
@@ -296,6 +298,7 @@ export function workbenchInstantiationService(): IInstantiationService {
instantiationService.stub(IUntitledEditorService, instantiationService.createInstance(UntitledEditorService));
instantiationService.stub(IStorageService, new TestStorageService());
instantiationService.stub(IWorkbenchLayoutService, new TestLayoutService());
instantiationService.stub(IElectronService, new TestElectronService());
instantiationService.stub(IModeService, instantiationService.createInstance(ModeServiceImpl));
instantiationService.stub(IHistoryService, new TestHistoryService());
instantiationService.stub(ITextResourcePropertiesService, new TestTextResourcePropertiesService(configService));
@@ -1309,16 +1312,72 @@ export class TestHostService implements IHostService {
readonly hasFocus: boolean = true;
readonly onDidChangeFocus: Event<boolean> = Event.None;
windowCount = Promise.resolve(1);
async restart(): Promise<void> { }
async reload(): Promise<void> { }
async closeWorkspace(): Promise<void> { }
async focus(): Promise<void> { }
async openEmptyWindow(options?: IOpenEmptyWindowOptions): Promise<void> { }
async openInWindow(toOpen: IWindowOpenable[], options?: IOpenInWindowOptions): Promise<void> { }
async openWindow(arg1?: IOpenEmptyWindowOptions | IWindowOpenable[], arg2?: IOpenWindowOptions): Promise<void> { }
async toggleFullScreen(): Promise<void> { }
}
export class TestElectronService implements IElectronService {
_serviceBrand: undefined;
onWindowOpen: Event<number> = Event.None;
onWindowMaximize: Event<number> = Event.None;
onWindowUnmaximize: Event<number> = Event.None;
onWindowFocus: Event<number> = Event.None;
onWindowBlur: Event<number> = Event.None;
windowCount = Promise.resolve(1);
getWindowCount(): Promise<number> { return this.windowCount; }
async getWindows(): Promise<IOpenedWindow[]> { return []; }
async getActiveWindowId(): Promise<number | undefined> { return undefined; }
openWindow(options?: IOpenEmptyWindowOptions): Promise<void>;
openWindow(toOpen: IWindowOpenable[], options?: IOpenWindowOptions): Promise<void>;
openWindow(arg1?: IOpenEmptyWindowOptions | IWindowOpenable[], arg2?: IOpenWindowOptions): Promise<void> {
throw new Error('Method not implemented.');
}
async toggleFullScreen(): Promise<void> { }
async handleTitleDoubleClick(): Promise<void> { }
async isMaximized(): Promise<boolean> { return true; }
async maximizeWindow(): Promise<void> { }
async unmaximizeWindow(): Promise<void> { }
async minimizeWindow(): Promise<void> { }
async isWindowFocused(): Promise<boolean> { return true; }
async focusWindow(options?: { windowId?: number | undefined; } | undefined): Promise<void> { }
async showMessageBox(options: Electron.MessageBoxOptions): Promise<Electron.MessageBoxReturnValue> { throw new Error('Method not implemented.'); }
async showSaveDialog(options: Electron.SaveDialogOptions): Promise<Electron.SaveDialogReturnValue> { throw new Error('Method not implemented.'); }
async showOpenDialog(options: Electron.OpenDialogOptions): Promise<Electron.OpenDialogReturnValue> { throw new Error('Method not implemented.'); }
async pickFileFolderAndOpen(options: INativeOpenDialogOptions): Promise<void> { }
async pickFileAndOpen(options: INativeOpenDialogOptions): Promise<void> { }
async pickFolderAndOpen(options: INativeOpenDialogOptions): Promise<void> { }
async pickWorkspaceAndOpen(options: INativeOpenDialogOptions): Promise<void> { }
async showItemInFolder(path: string): Promise<void> { }
async setRepresentedFilename(path: string): Promise<void> { }
async setDocumentEdited(edited: boolean): Promise<void> { }
async openExternal(url: string): Promise<boolean> { return false; }
async updateTouchBar(items: { id: string; title: string | { value: string; original: string; }; category?: string | { value: string; original: string; } | undefined; iconLocation?: { dark: UriComponents; light?: { readonly scheme: string; readonly authority: string; readonly path: string; readonly query: string; readonly fragment: string; readonly fsPath: string; with: {}; toString: {}; toJSON: {}; } | undefined; } | undefined; precondition?: { getType: {}; equals: {}; evaluate: {}; serialize: {}; keys: {}; map: {}; negate: {}; } | undefined; toggled?: { getType: {}; equals: {}; evaluate: {}; serialize: {}; keys: {}; map: {}; negate: {}; } | undefined; }[][]): Promise<void> { }
async newWindowTab(): Promise<void> { }
async showPreviousWindowTab(): Promise<void> { }
async showNextWindowTab(): Promise<void> { }
async moveWindowTabToNewWindow(): Promise<void> { }
async mergeAllWindowTabs(): Promise<void> { }
async toggleWindowTabsBar(): Promise<void> { }
async relaunch(options?: { addArgs?: string[] | undefined; removeArgs?: string[] | undefined; } | undefined): Promise<void> { }
async reload(): Promise<void> { }
async closeWorkspace(): Promise<void> { }
async closeWindow(): Promise<void> { }
async quit(): Promise<void> { }
async openDevTools(options?: Electron.OpenDevToolsOptions | undefined): Promise<void> { }
async toggleDevTools(): Promise<void> { }
async startCrashReporter(options: Electron.CrashReporterStartOptions): Promise<void> { }
async resolveProxy(url: string): Promise<string | undefined> { return undefined; }
async openExtensionDevelopmentHostWindow(args: minimist.ParsedArgs, env: IProcessEnvironment): Promise<void> { }
}
@@ -235,7 +235,6 @@ import 'vs/workbench/contrib/surveys/browser/languageSurveys.contribution';
import 'vs/workbench/contrib/welcome/overlay/browser/welcomeOverlay';
import 'vs/workbench/contrib/welcome/page/browser/welcomePage.contribution';
import 'vs/workbench/contrib/welcome/walkThrough/browser/walkThrough.contribution';
import 'vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.contribution';
// Call Hierarchy
import 'vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution';
+1 -1
View File
@@ -133,7 +133,7 @@ import 'vs/workbench/contrib/tasks/electron-browser/taskService';
import 'vs/workbench/contrib/userDataSync/electron-browser/userDataSync.contribution';
// Welcome
import 'vs/workbench/contrib/welcome/gettingStarted/electron-browser/openWebsite.contribution';
import 'vs/workbench/contrib/welcome/gettingStarted/electron-browser/gettingStarted.contribution';
// Configuration Exporter
import 'vs/workbench/contrib/configExporter/node/configurationExportHelper.contribution';
+3
View File
@@ -105,4 +105,7 @@ import 'vs/workbench/contrib/terminal/browser/terminalInstanceService';
// Tasks
import 'vs/workbench/contrib/tasks/browser/taskService';
// Welcome
import 'vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.contribution';
//#endregion