mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-03 09:35:40 -05:00
Merge from vscode a348d103d1256a06a2c9b3f9b406298a9fef6898 (#15681)
* Merge from vscode a348d103d1256a06a2c9b3f9b406298a9fef6898 * Fixes and cleanup * Distro * Fix hygiene yarn * delete no yarn lock changes file * Fix hygiene * Fix layer check * Fix CI * Skip lib checks * Remove tests deleted in vs code * Fix tests * Distro * Fix tests and add removed extension point * Skip failing notebook tests for now * Disable broken tests and cleanup build folder * Update yarn.lock and fix smoke tests * Bump sqlite * fix contributed actions and file spacing * Fix user data path * Update yarn.locks Co-authored-by: ADS Merger <karlb@microsoft.com>
This commit is contained in:
@@ -3,13 +3,14 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { isMacintosh, isLinux, isWeb, IProcessEnvironment } from 'vs/base/common/platform';
|
||||
import { isMacintosh, isLinux, isWeb, isNative } from 'vs/base/common/platform';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { NativeParsedArgs } from 'vs/platform/environment/common/argv';
|
||||
import { LogLevel } from 'vs/platform/log/common/log';
|
||||
import { PerformanceMark } from 'vs/base/common/performance';
|
||||
import { ISandboxConfiguration } from 'vs/base/parts/sandbox/common/sandboxTypes';
|
||||
|
||||
export const WindowMinimumSize = {
|
||||
WIDTH: 400,
|
||||
@@ -19,6 +20,14 @@ export const WindowMinimumSize = {
|
||||
|
||||
export interface IBaseOpenWindowsOptions {
|
||||
readonly forceReuseWindow?: boolean;
|
||||
/**
|
||||
* The remote authority to use when windows are opened with either
|
||||
* - no workspace (empty window)
|
||||
* - a workspace that is neither `file://` nor `vscode-remote://`
|
||||
* Use 'null' for a local window.
|
||||
* If not set, defaults to the remote authority of the current window.
|
||||
*/
|
||||
readonly remoteAuthority?: string | null;
|
||||
}
|
||||
|
||||
export interface IOpenWindowOptions extends IBaseOpenWindowsOptions {
|
||||
@@ -48,7 +57,6 @@ export interface IOpenedWindow {
|
||||
}
|
||||
|
||||
export interface IOpenEmptyWindowOptions extends IBaseOpenWindowsOptions {
|
||||
readonly remoteAuthority?: string;
|
||||
}
|
||||
|
||||
export type IWindowOpenable = IWorkspaceToOpen | IFolderToOpen | IFileToOpen;
|
||||
@@ -87,7 +95,7 @@ export function getMenuBarVisibility(configurationService: IConfigurationService
|
||||
const titleBarStyle = getTitleBarStyle(configurationService);
|
||||
const menuBarVisibility = configurationService.getValue<MenuBarVisibility | 'default'>('window.menuBarVisibility');
|
||||
|
||||
if (menuBarVisibility === 'default' || (titleBarStyle === 'native' && menuBarVisibility === 'compact')) {
|
||||
if (menuBarVisibility === 'default' || (titleBarStyle === 'native' && menuBarVisibility === 'compact') || (isMacintosh && isNative)) {
|
||||
return 'classic';
|
||||
} else {
|
||||
return menuBarVisibility;
|
||||
@@ -169,7 +177,7 @@ export interface IPathData {
|
||||
readonly openOnlyIfExists?: boolean;
|
||||
|
||||
// Specifies an optional id to override the editor used to edit the resource, e.g. custom editor.
|
||||
readonly overrideId?: string;
|
||||
readonly editorOverrideId?: string;
|
||||
}
|
||||
|
||||
export interface IPathsToWaitFor extends IPathsToWaitForData {
|
||||
@@ -211,8 +219,6 @@ export interface IColorScheme {
|
||||
}
|
||||
|
||||
export interface IWindowConfiguration {
|
||||
sessionId: string;
|
||||
|
||||
remoteAuthority?: string;
|
||||
|
||||
colorScheme: IColorScheme;
|
||||
@@ -224,32 +230,34 @@ export interface IWindowConfiguration {
|
||||
|
||||
export interface IOSConfiguration {
|
||||
readonly release: string;
|
||||
readonly hostname: string;
|
||||
}
|
||||
|
||||
export interface INativeWindowConfiguration extends IWindowConfiguration, NativeParsedArgs {
|
||||
export interface INativeWindowConfiguration extends IWindowConfiguration, NativeParsedArgs, ISandboxConfiguration {
|
||||
mainPid: number;
|
||||
|
||||
windowId: number;
|
||||
machineId: string;
|
||||
|
||||
appRoot: string;
|
||||
execPath: string;
|
||||
backupPath?: string;
|
||||
|
||||
nodeCachedDataDir?: string;
|
||||
homeDir: string;
|
||||
tmpDir: string;
|
||||
userDataDir: string;
|
||||
|
||||
partsSplashPath: string;
|
||||
|
||||
workspace?: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier;
|
||||
|
||||
isInitialStartup?: boolean;
|
||||
logLevel: LogLevel;
|
||||
zoomLevel?: number;
|
||||
|
||||
fullscreen?: boolean;
|
||||
maximized?: boolean;
|
||||
accessibilitySupport?: boolean;
|
||||
|
||||
perfMarks: PerformanceMark[];
|
||||
|
||||
userEnv: IProcessEnvironment;
|
||||
filesToWait?: IPathsToWaitFor;
|
||||
|
||||
os: IOSConfiguration;
|
||||
|
||||
1364
src/vs/platform/windows/electron-main/window.ts
Normal file
1364
src/vs/platform/windows/electron-main/window.ts
Normal file
File diff suppressed because it is too large
Load Diff
@@ -45,6 +45,14 @@ export interface IWindowState {
|
||||
display?: number;
|
||||
}
|
||||
|
||||
export const defaultWindowState = function (mode = WindowMode.Normal): IWindowState {
|
||||
return {
|
||||
width: 1024,
|
||||
height: 768,
|
||||
mode
|
||||
};
|
||||
};
|
||||
|
||||
export const enum WindowMode {
|
||||
Maximized,
|
||||
Normal,
|
||||
@@ -52,17 +60,21 @@ export const enum WindowMode {
|
||||
Fullscreen
|
||||
}
|
||||
|
||||
export interface ILoadEvent {
|
||||
workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | undefined;
|
||||
}
|
||||
|
||||
export interface ICodeWindow extends IDisposable {
|
||||
|
||||
readonly onLoad: Event<void>;
|
||||
readonly onReady: Event<void>;
|
||||
readonly onClose: Event<void>;
|
||||
readonly onDestroy: Event<void>;
|
||||
readonly onWillLoad: Event<ILoadEvent>;
|
||||
readonly onDidSignalReady: Event<void>;
|
||||
readonly onDidClose: Event<void>;
|
||||
readonly onDidDestroy: Event<void>;
|
||||
|
||||
readonly whenClosedOrLoaded: Promise<void>;
|
||||
|
||||
readonly id: number;
|
||||
readonly win: BrowserWindow;
|
||||
readonly win: BrowserWindow | null; /* `null` after being disposed */
|
||||
readonly config: INativeWindowConfiguration | undefined;
|
||||
|
||||
readonly openedWorkspace?: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier;
|
||||
@@ -113,6 +125,24 @@ export interface ICodeWindow extends IDisposable {
|
||||
serializeWindowState(): IWindowState;
|
||||
}
|
||||
|
||||
export const enum WindowError {
|
||||
|
||||
/**
|
||||
* Maps to the `unresponsive` event on a `BrowserWindow`.
|
||||
*/
|
||||
UNRESPONSIVE = 1,
|
||||
|
||||
/**
|
||||
* Maps to the `render-proces-gone` event on a `WebContents`.
|
||||
*/
|
||||
CRASHED = 2,
|
||||
|
||||
/**
|
||||
* Maps to the `did-fail-load` event on a `WebContents`.
|
||||
*/
|
||||
LOAD = 3
|
||||
}
|
||||
|
||||
export const IWindowsMainService = createDecorator<IWindowsMainService>('windowsMainService');
|
||||
|
||||
export interface IWindowsCountChangedEvent {
|
||||
@@ -124,11 +154,11 @@ export interface IWindowsMainService {
|
||||
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
readonly onWindowsCountChanged: Event<IWindowsCountChangedEvent>;
|
||||
readonly onDidChangeWindowsCount: Event<IWindowsCountChangedEvent>;
|
||||
|
||||
readonly onWindowOpened: Event<ICodeWindow>;
|
||||
readonly onWindowReady: Event<ICodeWindow>;
|
||||
readonly onWindowDestroyed: Event<ICodeWindow>;
|
||||
readonly onDidOpenWindow: Event<ICodeWindow>;
|
||||
readonly onDidSignalReadyWindow: Event<ICodeWindow>;
|
||||
readonly onDidDestroyWindow: Event<ICodeWindow>;
|
||||
|
||||
open(openConfig: IOpenConfiguration): ICodeWindow[];
|
||||
openEmptyWindow(openConfig: IOpenEmptyConfiguration, options?: IOpenEmptyWindowOptions): ICodeWindow[];
|
||||
@@ -167,6 +197,12 @@ export interface IOpenConfiguration extends IBaseOpenConfiguration {
|
||||
readonly gotoLineMode?: boolean;
|
||||
readonly initialStartup?: boolean;
|
||||
readonly noRecentEntry?: boolean;
|
||||
/**
|
||||
* The remote authority to use when windows are opened with either
|
||||
* - no workspace (empty window)
|
||||
* - a workspace that is neither `file://` nor `vscode-remote://`
|
||||
*/
|
||||
readonly remoteAuthority?: string;
|
||||
}
|
||||
|
||||
export interface IOpenEmptyConfiguration extends IBaseOpenConfiguration { }
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { statSync } from 'fs';
|
||||
import { release, hostname } from 'os';
|
||||
import product from 'vs/platform/product/common/product';
|
||||
import { mark, getMarks } from 'vs/base/common/performance';
|
||||
import { basename, normalize, join, posix } from 'vs/base/common/path';
|
||||
import { localize } from 'vs/nls';
|
||||
import { coalesce, distinct, firstOrDefault } from 'vs/base/common/arrays';
|
||||
@@ -12,15 +15,15 @@ import { IEmptyWindowBackupInfo } from 'vs/platform/backup/node/backup';
|
||||
import { IEnvironmentMainService } from 'vs/platform/environment/electron-main/environmentMainService';
|
||||
import { NativeParsedArgs } from 'vs/platform/environment/common/argv';
|
||||
import { IStateService } from 'vs/platform/state/node/state';
|
||||
import { CodeWindow } from 'vs/code/electron-main/window';
|
||||
import { BrowserWindow, MessageBoxOptions, WebContents } from 'electron';
|
||||
import { ILifecycleMainService, UnloadReason, LifecycleMainService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
|
||||
import { CodeWindow } from 'vs/platform/windows/electron-main/window';
|
||||
import { app, BrowserWindow, MessageBoxOptions, nativeTheme, WebContents } from 'electron';
|
||||
import { ILifecycleMainService, UnloadReason } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IWindowSettings, IPath, isFileToOpen, isWorkspaceToOpen, isFolderToOpen, IWindowOpenable, IOpenEmptyWindowOptions, IAddFoldersRequest, IPathsToWaitFor, INativeWindowConfiguration, INativeOpenFileRequest } from 'vs/platform/windows/common/windows';
|
||||
import { findWindowOnFile, findWindowOnWorkspaceOrFolder, findWindowOnExtensionDevelopmentPath } from 'vs/platform/windows/electron-main/windowsFinder';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import product from 'vs/platform/product/common/product';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { IWindowsMainService, IOpenConfiguration, IWindowsCountChangedEvent, ICodeWindow, IOpenEmptyConfiguration, OpenContext } from 'vs/platform/windows/electron-main/windows';
|
||||
import { IWorkspacesHistoryMainService } from 'vs/platform/workspaces/electron-main/workspacesHistoryMainService';
|
||||
import { IProcessEnvironment, isMacintosh } from 'vs/base/common/platform';
|
||||
@@ -33,14 +36,16 @@ import { getRemoteAuthority } from 'vs/platform/remote/common/remoteHosts';
|
||||
import { IWindowState, WindowsStateHandler } from 'vs/platform/windows/electron-main/windowsStateHandler';
|
||||
import { getSingleFolderWorkspaceIdentifier, getWorkspaceIdentifier, IWorkspacesManagementMainService } from 'vs/platform/workspaces/electron-main/workspacesManagementMainService';
|
||||
import { once } from 'vs/base/common/functional';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { IDialogMainService } from 'vs/platform/dialogs/electron-main/dialogMainService';
|
||||
import { withNullAsUndefined } from 'vs/base/common/types';
|
||||
import { assertIsDefined, withNullAsUndefined } from 'vs/base/common/types';
|
||||
import { isWindowsDriveLetter, toSlashes, parseLineAndColumnAware, sanitizeFilePath } from 'vs/base/common/extpath';
|
||||
import { CharCode } from 'vs/base/common/charCode';
|
||||
import { getPathLabel } from 'vs/base/common/labels';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { cwd } from 'vs/base/common/process';
|
||||
import { IProtocolMainService } from 'vs/platform/protocol/electron-main/protocol';
|
||||
|
||||
//#region Helper Interfaces
|
||||
|
||||
@@ -68,6 +73,10 @@ interface IOpenBrowserWindowOptions {
|
||||
interface IPathResolveOptions {
|
||||
readonly ignoreFileNotFound?: boolean;
|
||||
readonly gotoLineMode?: boolean;
|
||||
readonly forceOpenWorkspaceAsFile?: boolean;
|
||||
/**
|
||||
* The remoteAuthority to use if the URL to open is neither file nor vscode-remote
|
||||
*/
|
||||
readonly remoteAuthority?: string;
|
||||
}
|
||||
|
||||
@@ -118,17 +127,17 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
|
||||
private static readonly WINDOWS: ICodeWindow[] = [];
|
||||
|
||||
private readonly _onWindowOpened = this._register(new Emitter<ICodeWindow>());
|
||||
readonly onWindowOpened = this._onWindowOpened.event;
|
||||
private readonly _onDidOpenWindow = this._register(new Emitter<ICodeWindow>());
|
||||
readonly onDidOpenWindow = this._onDidOpenWindow.event;
|
||||
|
||||
private readonly _onWindowReady = this._register(new Emitter<ICodeWindow>());
|
||||
readonly onWindowReady = this._onWindowReady.event;
|
||||
private readonly _onDidSignalReadyWindow = this._register(new Emitter<ICodeWindow>());
|
||||
readonly onDidSignalReadyWindow = this._onDidSignalReadyWindow.event;
|
||||
|
||||
private readonly _onWindowDestroyed = this._register(new Emitter<ICodeWindow>());
|
||||
readonly onWindowDestroyed = this._onWindowDestroyed.event;
|
||||
private readonly _onDidDestroyWindow = this._register(new Emitter<ICodeWindow>());
|
||||
readonly onDidDestroyWindow = this._onDidDestroyWindow.event;
|
||||
|
||||
private readonly _onWindowsCountChanged = this._register(new Emitter<IWindowsCountChangedEvent>());
|
||||
readonly onWindowsCountChanged = this._onWindowsCountChanged.event;
|
||||
private readonly _onDidChangeWindowsCount = this._register(new Emitter<IWindowsCountChangedEvent>());
|
||||
readonly onDidChangeWindowsCount = this._onDidChangeWindowsCount.event;
|
||||
|
||||
private readonly windowsStateHandler = this._register(new WindowsStateHandler(this, this.stateService, this.lifecycleMainService, this.logService, this.configurationService));
|
||||
|
||||
@@ -137,7 +146,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
private readonly initialUserEnv: IProcessEnvironment,
|
||||
@ILogService private readonly logService: ILogService,
|
||||
@IStateService private readonly stateService: IStateService,
|
||||
@IEnvironmentMainService private readonly environmentService: IEnvironmentMainService,
|
||||
@IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService,
|
||||
@ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService,
|
||||
@IBackupMainService private readonly backupMainService: IBackupMainService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@@ -145,31 +154,49 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
@IWorkspacesManagementMainService private readonly workspacesManagementMainService: IWorkspacesManagementMainService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IDialogMainService private readonly dialogMainService: IDialogMainService,
|
||||
@IFileService private readonly fileService: IFileService
|
||||
@IFileService private readonly fileService: IFileService,
|
||||
@IProductService private readonly productService: IProductService,
|
||||
@IProtocolMainService private readonly protocolMainService: IProtocolMainService
|
||||
) {
|
||||
super();
|
||||
|
||||
this.lifecycleMainService.when(LifecycleMainPhase.Ready).then(() => this.registerListeners());
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
|
||||
// Signal a window is ready after having entered a workspace
|
||||
this._register(this.workspacesManagementMainService.onWorkspaceEntered(event => this._onWindowReady.fire(event.window)));
|
||||
this._register(this.workspacesManagementMainService.onDidEnterWorkspace(event => this._onDidSignalReadyWindow.fire(event.window)));
|
||||
|
||||
// Update valid roots in protocol service for extension dev windows
|
||||
this._register(this.onDidSignalReadyWindow(window => {
|
||||
if (window.config?.extensionDevelopmentPath || window.config?.extensionTestsPath) {
|
||||
const disposables = new DisposableStore();
|
||||
disposables.add(Event.any(window.onDidClose, window.onDidDestroy)(() => disposables.dispose()));
|
||||
|
||||
// Allow access to extension development path
|
||||
if (window.config.extensionDevelopmentPath) {
|
||||
for (const extensionDevelopmentPath of window.config.extensionDevelopmentPath) {
|
||||
disposables.add(this.protocolMainService.addValidFileRoot(URI.file(extensionDevelopmentPath)));
|
||||
}
|
||||
}
|
||||
|
||||
// Allow access to extension tests path
|
||||
if (window.config.extensionTestsPath) {
|
||||
disposables.add(this.protocolMainService.addValidFileRoot(URI.file(window.config.extensionTestsPath)));
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
openEmptyWindow(openConfig: IOpenEmptyConfiguration, options?: IOpenEmptyWindowOptions): ICodeWindow[] {
|
||||
let cli = this.environmentService.args;
|
||||
const remote = options?.remoteAuthority;
|
||||
if (cli && (cli.remote !== remote)) {
|
||||
cli = { ...cli, remote };
|
||||
}
|
||||
|
||||
let cli = this.environmentMainService.args;
|
||||
const remoteAuthority = options?.remoteAuthority || undefined;
|
||||
const forceEmpty = true;
|
||||
const forceReuseWindow = options?.forceReuseWindow;
|
||||
const forceNewWindow = !forceReuseWindow;
|
||||
|
||||
return this.open({ ...openConfig, cli, forceEmpty, forceNewWindow, forceReuseWindow });
|
||||
return this.open({ ...openConfig, cli, forceEmpty, forceNewWindow, forceReuseWindow, remoteAuthority });
|
||||
}
|
||||
|
||||
open(openConfig: IOpenConfiguration): ICodeWindow[] {
|
||||
@@ -298,11 +325,11 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
const recents: IRecent[] = [];
|
||||
for (const pathToOpen of pathsToOpen) {
|
||||
if (isWorkspacePathToOpen(pathToOpen)) {
|
||||
recents.push({ label: pathToOpen.label, workspace: pathToOpen.workspace });
|
||||
recents.push({ label: pathToOpen.label, workspace: pathToOpen.workspace, remoteAuthority: pathToOpen.remoteAuthority });
|
||||
} else if (isSingleFolderWorkspacePathToOpen(pathToOpen)) {
|
||||
recents.push({ label: pathToOpen.label, folderUri: pathToOpen.workspace.uri });
|
||||
recents.push({ label: pathToOpen.label, folderUri: pathToOpen.workspace.uri, remoteAuthority: pathToOpen.remoteAuthority });
|
||||
} else if (pathToOpen.fileUri) {
|
||||
recents.push({ label: pathToOpen.label, fileUri: pathToOpen.fileUri });
|
||||
recents.push({ label: pathToOpen.label, fileUri: pathToOpen.fileUri, remoteAuthority: pathToOpen.remoteAuthority });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -314,7 +341,15 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
// process can continue. We do this by deleting the waitMarkerFilePath.
|
||||
const waitMarkerFileURI = openConfig.waitMarkerFileURI;
|
||||
if (openConfig.context === OpenContext.CLI && waitMarkerFileURI && usedWindows.length === 1 && usedWindows[0]) {
|
||||
usedWindows[0].whenClosedOrLoaded.then(() => this.fileService.del(waitMarkerFileURI), () => undefined);
|
||||
(async () => {
|
||||
await usedWindows[0].whenClosedOrLoaded;
|
||||
|
||||
try {
|
||||
await this.fileService.del(waitMarkerFileURI);
|
||||
} catch (error) {
|
||||
// ignore - could have been deleted from the window already
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
return usedWindows;
|
||||
@@ -507,7 +542,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
emptyToOpen++;
|
||||
}
|
||||
|
||||
const remoteAuthority = filesToOpen ? filesToOpen.remoteAuthority : (openConfig.cli && openConfig.cli.remote || undefined);
|
||||
const remoteAuthority = filesToOpen ? filesToOpen.remoteAuthority : openConfig.remoteAuthority;
|
||||
|
||||
for (let i = 0; i < emptyToOpen; i++) {
|
||||
addUsedWindow(this.doOpenEmpty(openConfig, openFolderInNewWindow, remoteAuthority, filesToOpen), !!filesToOpen);
|
||||
@@ -650,7 +685,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
|
||||
private doExtractPathsFromAPI(openConfig: IOpenConfiguration): IPathToOpen[] {
|
||||
const pathsToOpen: IPathToOpen[] = [];
|
||||
const pathResolveOptions: IPathResolveOptions = { gotoLineMode: openConfig.gotoLineMode };
|
||||
const pathResolveOptions: IPathResolveOptions = { gotoLineMode: openConfig.gotoLineMode, remoteAuthority: openConfig.remoteAuthority };
|
||||
for (const pathToOpen of coalesce(openConfig.urisToOpen || [])) {
|
||||
const path = this.resolveOpenable(pathToOpen, pathResolveOptions);
|
||||
|
||||
@@ -665,12 +700,12 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
const uri = this.resourceFromOpenable(pathToOpen);
|
||||
|
||||
const options: MessageBoxOptions = {
|
||||
title: product.nameLong,
|
||||
title: this.productService.nameLong,
|
||||
type: 'info',
|
||||
buttons: [localize('ok', "OK")],
|
||||
message: uri.scheme === Schemas.file ? localize('pathNotExistTitle', "Path does not exist") : localize('uriInvalidTitle', "URI can not be opened"),
|
||||
detail: uri.scheme === Schemas.file ?
|
||||
localize('pathNotExistDetail', "The path '{0}' does not seem to exist anymore on disk.", getPathLabel(uri.fsPath, this.environmentService)) :
|
||||
localize('pathNotExistDetail', "The path '{0}' does not seem to exist anymore on disk.", getPathLabel(uri.fsPath, this.environmentMainService)) :
|
||||
localize('uriInvalidDetail', "The URI '{0}' is not valid and can not be opened.", uri.toString()),
|
||||
noLink: true
|
||||
};
|
||||
@@ -684,7 +719,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
|
||||
private doExtractPathsFromCLI(cli: NativeParsedArgs): IPath[] {
|
||||
const pathsToOpen: IPathToOpen[] = [];
|
||||
const pathResolveOptions: IPathResolveOptions = { ignoreFileNotFound: true, gotoLineMode: cli.goto, remoteAuthority: cli.remote || undefined };
|
||||
const pathResolveOptions: IPathResolveOptions = { ignoreFileNotFound: true, gotoLineMode: cli.goto, remoteAuthority: cli.remote || undefined, forceOpenWorkspaceAsFile: false };
|
||||
|
||||
// folder uris
|
||||
const folderUris = cli['folder-uri'];
|
||||
@@ -717,12 +752,11 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
// folder or file paths
|
||||
const cliPaths = cli._;
|
||||
for (const cliPath of cliPaths) {
|
||||
const path = this.doResolveFileOpenable(cliPath, pathResolveOptions);
|
||||
const path = pathResolveOptions.remoteAuthority ? this.doResolvePathRemote(cliPath, pathResolveOptions) : this.doResolveFilePath(cliPath, pathResolveOptions);
|
||||
if (path) {
|
||||
pathsToOpen.push(path);
|
||||
}
|
||||
}
|
||||
|
||||
return pathsToOpen;
|
||||
}
|
||||
|
||||
@@ -819,7 +853,10 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
// handle file:// openables with some extra validation
|
||||
let uri = this.resourceFromOpenable(openable);
|
||||
if (uri.scheme === Schemas.file) {
|
||||
return this.doResolveFileOpenable(openable, options);
|
||||
if (isFileToOpen(openable)) {
|
||||
options = { ...options, forceOpenWorkspaceAsFile: true };
|
||||
}
|
||||
return this.doResolveFilePath(uri.fsPath, options);
|
||||
}
|
||||
|
||||
// handle non file:// openables
|
||||
@@ -829,8 +866,8 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
private doResolveRemoteOpenable(openable: IWindowOpenable, options: IPathResolveOptions): IPathToOpen | undefined {
|
||||
let uri = this.resourceFromOpenable(openable);
|
||||
|
||||
// open remote if either specified in the cli or if it's a remotehost URI
|
||||
const remoteAuthority = options.remoteAuthority || getRemoteAuthority(uri);
|
||||
// use remote authority from vscode
|
||||
const remoteAuthority = getRemoteAuthority(uri) || options.remoteAuthority;
|
||||
|
||||
// normalize URI
|
||||
uri = removeTrailingPathSeparator(normalizePath(uri));
|
||||
@@ -867,88 +904,25 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
return openable.fileUri;
|
||||
}
|
||||
|
||||
private doResolveFileOpenable(path: string, options: IPathResolveOptions): IPathToOpen | undefined;
|
||||
private doResolveFileOpenable(openable: IWindowOpenable, options: IPathResolveOptions): IPathToOpen | undefined;
|
||||
private doResolveFileOpenable(pathOrOpenable: string | IWindowOpenable, options: IPathResolveOptions): IPathToOpen | undefined {
|
||||
let path: string;
|
||||
let forceOpenWorkspaceAsFile = false;
|
||||
if (typeof pathOrOpenable === 'string') {
|
||||
path = pathOrOpenable;
|
||||
} else {
|
||||
path = this.resourceFromOpenable(pathOrOpenable).fsPath;
|
||||
forceOpenWorkspaceAsFile = isFileToOpen(pathOrOpenable);
|
||||
}
|
||||
private doResolveFilePath(path: string, options: IPathResolveOptions): IPathToOpen | undefined {
|
||||
|
||||
// Extract line/col information from path
|
||||
let lineNumber: number | undefined;
|
||||
let columnNumber: number | undefined;
|
||||
|
||||
if (options.gotoLineMode) {
|
||||
const parsedPath = parseLineAndColumnAware(path);
|
||||
lineNumber = parsedPath.line;
|
||||
columnNumber = parsedPath.column;
|
||||
|
||||
path = parsedPath.path;
|
||||
({ path, line: lineNumber, column: columnNumber } = parseLineAndColumnAware(path));
|
||||
}
|
||||
|
||||
// With remote: resolve path as remote URI
|
||||
const remoteAuthority = options.remoteAuthority;
|
||||
if (remoteAuthority) {
|
||||
return this.doResolvePathRemote(path, remoteAuthority, forceOpenWorkspaceAsFile);
|
||||
}
|
||||
|
||||
// Without remote: resolve path as local URI
|
||||
return this.doResolvePathLocal(path, options, lineNumber, columnNumber, forceOpenWorkspaceAsFile);
|
||||
}
|
||||
|
||||
private doResolvePathRemote(path: string, remoteAuthority: string, forceOpenWorkspaceAsFile?: boolean): IPathToOpen | undefined {
|
||||
const first = path.charCodeAt(0);
|
||||
|
||||
// make absolute
|
||||
if (first !== CharCode.Slash) {
|
||||
if (isWindowsDriveLetter(first) && path.charCodeAt(path.charCodeAt(1)) === CharCode.Colon) {
|
||||
path = toSlashes(path);
|
||||
}
|
||||
|
||||
path = `/${path}`;
|
||||
}
|
||||
|
||||
const uri = URI.from({ scheme: Schemas.vscodeRemote, authority: remoteAuthority, path: path });
|
||||
|
||||
// guess the file type:
|
||||
// - if it ends with a slash it's a folder
|
||||
// - if it has a file extension, it's a file or a workspace
|
||||
// - by defaults it's a folder
|
||||
if (path.charCodeAt(path.length - 1) !== CharCode.Slash) {
|
||||
|
||||
// file name ends with .code-workspace
|
||||
if (hasWorkspaceFileExtension(path)) {
|
||||
if (forceOpenWorkspaceAsFile) {
|
||||
return { fileUri: uri, remoteAuthority };
|
||||
}
|
||||
return { workspace: getWorkspaceIdentifier(uri), remoteAuthority };
|
||||
}
|
||||
|
||||
// file name starts with a dot or has an file extension
|
||||
else if (posix.basename(path).indexOf('.') !== -1) {
|
||||
return { fileUri: uri, remoteAuthority };
|
||||
}
|
||||
}
|
||||
|
||||
return { workspace: getSingleFolderWorkspaceIdentifier(uri), remoteAuthority };
|
||||
}
|
||||
|
||||
private doResolvePathLocal(path: string, options: IPathResolveOptions, lineNumber: number | undefined, columnNumber: number | undefined, forceOpenWorkspaceAsFile?: boolean): IPathToOpen | undefined {
|
||||
|
||||
// Ensure the path is normalized and absolute
|
||||
path = sanitizeFilePath(normalize(path), process.env['VSCODE_CWD'] || process.cwd());
|
||||
path = sanitizeFilePath(normalize(path), cwd());
|
||||
|
||||
try {
|
||||
const pathStat = statSync(path);
|
||||
if (pathStat.isFile()) {
|
||||
|
||||
// Workspace (unless disabled via flag)
|
||||
if (!forceOpenWorkspaceAsFile) {
|
||||
if (!options.forceOpenWorkspaceAsFile) {
|
||||
const workspace = this.workspacesManagementMainService.resolveLocalWorkspaceSync(URI.file(path));
|
||||
if (workspace) {
|
||||
return { workspace: { id: workspace.id, configPath: workspace.configPath }, remoteAuthority: workspace.remoteAuthority, exists: true };
|
||||
@@ -972,7 +946,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
this.workspacesHistoryMainService.removeRecentlyOpened([fileUri]);
|
||||
|
||||
// assume this is a file that does not yet exist
|
||||
if (options?.ignoreFileNotFound) {
|
||||
if (options.ignoreFileNotFound) {
|
||||
return { fileUri, exists: false };
|
||||
}
|
||||
}
|
||||
@@ -980,6 +954,52 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private doResolvePathRemote(path: string, options: IPathResolveOptions): IPathToOpen | undefined {
|
||||
const first = path.charCodeAt(0);
|
||||
const remoteAuthority = options.remoteAuthority;
|
||||
|
||||
// Extract line/col information from path
|
||||
let lineNumber: number | undefined;
|
||||
let columnNumber: number | undefined;
|
||||
|
||||
if (options.gotoLineMode) {
|
||||
({ path, line: lineNumber, column: columnNumber } = parseLineAndColumnAware(path));
|
||||
}
|
||||
|
||||
// make absolute
|
||||
if (first !== CharCode.Slash) {
|
||||
if (isWindowsDriveLetter(first) && path.charCodeAt(path.charCodeAt(1)) === CharCode.Colon) {
|
||||
path = toSlashes(path);
|
||||
}
|
||||
|
||||
path = `/${path}`;
|
||||
}
|
||||
|
||||
const uri = URI.from({ scheme: Schemas.vscodeRemote, authority: remoteAuthority, path: path });
|
||||
|
||||
// guess the file type:
|
||||
// - if it ends with a slash it's a folder
|
||||
// - if in goto line mode or if it has a file extension, it's a file or a workspace
|
||||
// - by defaults it's a folder
|
||||
if (path.charCodeAt(path.length - 1) !== CharCode.Slash) {
|
||||
|
||||
// file name ends with .code-workspace
|
||||
if (hasWorkspaceFileExtension(path)) {
|
||||
if (options.forceOpenWorkspaceAsFile) {
|
||||
return { fileUri: uri, lineNumber, columnNumber, remoteAuthority: options.remoteAuthority };
|
||||
}
|
||||
return { workspace: getWorkspaceIdentifier(uri), remoteAuthority };
|
||||
}
|
||||
|
||||
// file name starts with a dot or has an file extension
|
||||
else if (options.gotoLineMode || posix.basename(path).indexOf('.') !== -1) {
|
||||
return { fileUri: uri, lineNumber, columnNumber, remoteAuthority };
|
||||
}
|
||||
}
|
||||
|
||||
return { workspace: getSingleFolderWorkspaceIdentifier(uri), remoteAuthority };
|
||||
}
|
||||
|
||||
private shouldOpenNewWindow(openConfig: IOpenConfiguration): { openFolderInNewWindow: boolean; openFilesInNewWindow: boolean; } {
|
||||
|
||||
// let the user settings override how folders are open in a new window or same window unless we are forced
|
||||
@@ -1060,17 +1080,18 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
}
|
||||
}
|
||||
|
||||
let authority = '';
|
||||
let remoteAuthority = openConfig.remoteAuthority;
|
||||
for (const extensionDevelopmentPath of extensionDevelopmentPaths) {
|
||||
if (extensionDevelopmentPath.match(/^[a-zA-Z][a-zA-Z0-9\+\-\.]+:/)) {
|
||||
const url = URI.parse(extensionDevelopmentPath);
|
||||
if (url.scheme === Schemas.vscodeRemote) {
|
||||
if (authority) {
|
||||
if (url.authority !== authority) {
|
||||
const extensionDevelopmentPathRemoteAuthority = getRemoteAuthority(url);
|
||||
if (extensionDevelopmentPathRemoteAuthority) {
|
||||
if (remoteAuthority) {
|
||||
if (extensionDevelopmentPathRemoteAuthority !== remoteAuthority) {
|
||||
this.logService.error('more than one extension development path authority');
|
||||
}
|
||||
} else {
|
||||
authority = url.authority;
|
||||
remoteAuthority = extensionDevelopmentPathRemoteAuthority;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1086,7 +1107,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
return false;
|
||||
}
|
||||
|
||||
return uri.authority === authority;
|
||||
return getRemoteAuthority(uri) === remoteAuthority;
|
||||
});
|
||||
|
||||
folderUris = folderUris.filter(folderUriStr => {
|
||||
@@ -1095,7 +1116,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
return false;
|
||||
}
|
||||
|
||||
return folderUri ? folderUri.authority === authority : false;
|
||||
return folderUri ? getRemoteAuthority(folderUri) === remoteAuthority : false;
|
||||
});
|
||||
|
||||
fileUris = fileUris.filter(fileUriStr => {
|
||||
@@ -1104,18 +1125,14 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
return false;
|
||||
}
|
||||
|
||||
return fileUri ? fileUri.authority === authority : false;
|
||||
return fileUri ? getRemoteAuthority(fileUri) === remoteAuthority : false;
|
||||
});
|
||||
|
||||
openConfig.cli._ = cliArgs;
|
||||
openConfig.cli['folder-uri'] = folderUris;
|
||||
openConfig.cli['file-uri'] = fileUris;
|
||||
|
||||
// if there are no files or folders cli args left, use the "remote" cli argument
|
||||
const noFilesOrFolders = !cliArgs.length && !folderUris.length && !fileUris.length;
|
||||
if (noFilesOrFolders && authority) {
|
||||
openConfig.cli.remote = authority;
|
||||
}
|
||||
|
||||
// Open it
|
||||
const openArgs: IOpenConfiguration = {
|
||||
@@ -1125,40 +1142,68 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
forceEmpty: noFilesOrFolders,
|
||||
userEnv: openConfig.userEnv,
|
||||
noRecentEntry: true,
|
||||
waitMarkerFileURI: openConfig.waitMarkerFileURI
|
||||
waitMarkerFileURI: openConfig.waitMarkerFileURI,
|
||||
remoteAuthority
|
||||
};
|
||||
|
||||
return this.open(openArgs);
|
||||
}
|
||||
|
||||
private openInBrowserWindow(options: IOpenBrowserWindowOptions): ICodeWindow {
|
||||
const windowConfig = this.configurationService.getValue<IWindowSettings | undefined>('window');
|
||||
|
||||
// Build `INativeWindowConfiguration` from config and options
|
||||
const configuration = { ...options.cli } as INativeWindowConfiguration;
|
||||
configuration.appRoot = this.environmentService.appRoot;
|
||||
configuration.machineId = this.machineId;
|
||||
configuration.nodeCachedDataDir = this.environmentService.nodeCachedDataDir;
|
||||
configuration.mainPid = process.pid;
|
||||
configuration.execPath = process.execPath;
|
||||
configuration.userEnv = { ...this.initialUserEnv, ...options.userEnv };
|
||||
configuration.isInitialStartup = options.initialStartup;
|
||||
configuration.workspace = options.workspace;
|
||||
configuration.remoteAuthority = options.remoteAuthority;
|
||||
// Build up the window configuration from provided options, config and environment
|
||||
const configuration: INativeWindowConfiguration = {
|
||||
|
||||
const filesToOpen = options.filesToOpen;
|
||||
if (filesToOpen) {
|
||||
configuration.filesToOpenOrCreate = filesToOpen.filesToOpenOrCreate;
|
||||
configuration.filesToDiff = filesToOpen.filesToDiff;
|
||||
configuration.filesToWait = filesToOpen.filesToWait;
|
||||
}
|
||||
// Inherit CLI arguments from environment and/or
|
||||
// the specific properties from this launch if provided
|
||||
...this.environmentMainService.args,
|
||||
...options.cli,
|
||||
|
||||
// if we know the backup folder upfront (for empty windows to restore), we can set it
|
||||
// directly here which helps for restoring UI state associated with that window.
|
||||
// For all other cases we first call into registerEmptyWindowBackupSync() to set it before
|
||||
// loading the window.
|
||||
if (options.emptyWindowBackupInfo) {
|
||||
configuration.backupPath = join(this.environmentService.backupHome, options.emptyWindowBackupInfo.backupFolder);
|
||||
}
|
||||
machineId: this.machineId,
|
||||
|
||||
windowId: -1, // Will be filled in by the window once loaded later
|
||||
|
||||
mainPid: process.pid,
|
||||
|
||||
appRoot: this.environmentMainService.appRoot,
|
||||
execPath: process.execPath,
|
||||
nodeCachedDataDir: this.environmentMainService.nodeCachedDataDir,
|
||||
partsSplashPath: join(this.environmentMainService.userDataPath, 'rapid_render.json'),
|
||||
// If we know the backup folder upfront (for empty windows to restore), we can set it
|
||||
// directly here which helps for restoring UI state associated with that window.
|
||||
// For all other cases we first call into registerEmptyWindowBackupSync() to set it before
|
||||
// loading the window.
|
||||
backupPath: options.emptyWindowBackupInfo ? join(this.environmentMainService.backupHome, options.emptyWindowBackupInfo.backupFolder) : undefined,
|
||||
|
||||
homeDir: this.environmentMainService.userHome.fsPath,
|
||||
tmpDir: this.environmentMainService.tmpDir.fsPath,
|
||||
userDataDir: this.environmentMainService.userDataPath,
|
||||
|
||||
remoteAuthority: options.remoteAuthority,
|
||||
workspace: options.workspace,
|
||||
userEnv: { ...this.initialUserEnv, ...options.userEnv },
|
||||
|
||||
filesToOpenOrCreate: options.filesToOpen?.filesToOpenOrCreate,
|
||||
filesToDiff: options.filesToOpen?.filesToDiff,
|
||||
filesToWait: options.filesToOpen?.filesToWait,
|
||||
|
||||
logLevel: this.logService.getLevel(),
|
||||
logsPath: this.environmentMainService.logsPath,
|
||||
|
||||
product,
|
||||
isInitialStartup: options.initialStartup,
|
||||
perfMarks: getMarks(),
|
||||
os: { release: release(), hostname: hostname() },
|
||||
zoomLevel: typeof windowConfig?.zoomLevel === 'number' ? windowConfig.zoomLevel : undefined,
|
||||
|
||||
autoDetectHighContrast: windowConfig?.autoDetectHighContrast ?? true,
|
||||
accessibilitySupport: app.accessibilitySupportEnabled,
|
||||
colorScheme: {
|
||||
dark: nativeTheme.shouldUseDarkColors,
|
||||
highContrast: nativeTheme.shouldUseInvertedColorScheme || nativeTheme.shouldUseHighContrastColors
|
||||
}
|
||||
};
|
||||
|
||||
let window: ICodeWindow | undefined;
|
||||
if (!options.forceNewWindow && !options.forceNewTabbedWindow) {
|
||||
@@ -1173,11 +1218,13 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
const state = this.windowsStateHandler.getNewWindowState(configuration);
|
||||
|
||||
// Create the window
|
||||
const createdWindow = window = this.instantiationService.createInstance(CodeWindow, {
|
||||
mark('code/willCreateCodeWindow');
|
||||
const createdWindow = window = this.instantiationService.createInstance(<any>CodeWindow, { // {{SQL CARBON EDIT}} Fix weird compile error...
|
||||
state,
|
||||
extensionDevelopmentPath: configuration.extensionDevelopmentPath,
|
||||
isExtensionTestHost: !!configuration.extensionTestsPath
|
||||
});
|
||||
mark('code/didCreateCodeWindow');
|
||||
|
||||
// Add as window tab if configured (macOS only)
|
||||
if (options.forceNewTabbedWindow) {
|
||||
@@ -1191,20 +1238,22 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
WindowsMainService.WINDOWS.push(createdWindow);
|
||||
|
||||
// Indicate new window via event
|
||||
this._onWindowOpened.fire(createdWindow);
|
||||
this._onDidOpenWindow.fire(createdWindow);
|
||||
|
||||
// Indicate number change via event
|
||||
this._onWindowsCountChanged.fire({ oldCount: this.getWindowCount() - 1, newCount: this.getWindowCount() });
|
||||
this._onDidChangeWindowsCount.fire({ oldCount: this.getWindowCount() - 1, newCount: this.getWindowCount() });
|
||||
|
||||
// Window Events
|
||||
once(createdWindow.onReady)(() => this._onWindowReady.fire(createdWindow));
|
||||
once(createdWindow.onClose)(() => this.onWindowClosed(createdWindow));
|
||||
once(createdWindow.onDestroy)(() => this._onWindowDestroyed.fire(createdWindow));
|
||||
createdWindow.win.webContents.removeAllListeners('devtools-reload-page'); // remove built in listener so we can handle this on our own
|
||||
createdWindow.win.webContents.on('devtools-reload-page', () => this.lifecycleMainService.reload(createdWindow));
|
||||
once(createdWindow.onDidSignalReady)(() => this._onDidSignalReadyWindow.fire(createdWindow));
|
||||
once(createdWindow.onDidClose)(() => this.onWindowClosed(createdWindow));
|
||||
once(createdWindow.onDidDestroy)(() => this._onDidDestroyWindow.fire(createdWindow));
|
||||
|
||||
const webContents = assertIsDefined(createdWindow.win?.webContents);
|
||||
webContents.removeAllListeners('devtools-reload-page'); // remove built in listener so we can handle this on our own
|
||||
webContents.on('devtools-reload-page', () => this.lifecycleMainService.reload(createdWindow));
|
||||
|
||||
// Lifecycle
|
||||
(this.lifecycleMainService as LifecycleMainService).registerWindow(createdWindow);
|
||||
this.lifecycleMainService.registerWindow(createdWindow);
|
||||
}
|
||||
|
||||
// Existing window
|
||||
@@ -1223,6 +1272,10 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
}
|
||||
}
|
||||
|
||||
// Update window identifier and session now
|
||||
// that we have the window object in hand.
|
||||
configuration.windowId = window.id;
|
||||
|
||||
// If the window was already loaded, make sure to unload it
|
||||
// first and only load the new configuration if that was
|
||||
// not vetoed
|
||||
@@ -1264,7 +1317,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
WindowsMainService.WINDOWS.splice(index, 1);
|
||||
|
||||
// Emit
|
||||
this._onWindowsCountChanged.fire({ oldCount: this.getWindowCount() + 1, newCount: this.getWindowCount() });
|
||||
this._onDidChangeWindowsCount.fire({ oldCount: this.getWindowCount() + 1, newCount: this.getWindowCount() });
|
||||
}
|
||||
|
||||
getFocusedWindow(): ICodeWindow | undefined {
|
||||
|
||||
@@ -8,13 +8,12 @@ import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { isMacintosh } from 'vs/base/common/platform';
|
||||
import { extUriBiasedIgnorePathCase } from 'vs/base/common/resources';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { defaultWindowState } from 'vs/code/electron-main/window';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IStateService } from 'vs/platform/state/node/state';
|
||||
import { INativeWindowConfiguration, IWindowSettings } from 'vs/platform/windows/common/windows';
|
||||
import { ICodeWindow, IWindowsMainService, IWindowState as IWindowUIState, WindowMode } from 'vs/platform/windows/electron-main/windows';
|
||||
import { defaultWindowState, ICodeWindow, IWindowsMainService, IWindowState as IWindowUIState, WindowMode } from 'vs/platform/windows/electron-main/windows';
|
||||
import { isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
|
||||
|
||||
export interface IWindowState {
|
||||
@@ -84,9 +83,9 @@ export class WindowsStateHandler extends Disposable {
|
||||
});
|
||||
|
||||
// Handle various lifecycle events around windows
|
||||
this.lifecycleMainService.onBeforeWindowClose(window => this.onBeforeWindowClose(window));
|
||||
this.lifecycleMainService.onBeforeCloseWindow(window => this.onBeforeCloseWindow(window));
|
||||
this.lifecycleMainService.onBeforeShutdown(() => this.onBeforeShutdown());
|
||||
this.windowsMainService.onWindowsCountChanged(e => {
|
||||
this.windowsMainService.onDidChangeWindowsCount(e => {
|
||||
if (e.newCount - e.oldCount > 0) {
|
||||
// clear last closed window state when a new window opens. this helps on macOS where
|
||||
// otherwise closing the last window, opening a new window and then quitting would
|
||||
@@ -96,16 +95,16 @@ export class WindowsStateHandler extends Disposable {
|
||||
});
|
||||
|
||||
// try to save state before destroy because close will not fire
|
||||
this.windowsMainService.onWindowDestroyed(window => this.onBeforeWindowClose(window));
|
||||
this.windowsMainService.onDidDestroyWindow(window => this.onBeforeCloseWindow(window));
|
||||
}
|
||||
|
||||
// Note that onBeforeShutdown() and onBeforeWindowClose() are fired in different order depending on the OS:
|
||||
// Note that onBeforeShutdown() and onBeforeCloseWindow() are fired in different order depending on the OS:
|
||||
// - macOS: since the app will not quit when closing the last window, you will always first get
|
||||
// the onBeforeShutdown() event followed by N onBeforeWindowClose() events for each window
|
||||
// the onBeforeShutdown() event followed by N onBeforeCloseWindow() events for each window
|
||||
// - other: on other OS, closing the last window will quit the app so the order depends on the
|
||||
// user interaction: closing the last window will first trigger onBeforeWindowClose()
|
||||
// user interaction: closing the last window will first trigger onBeforeCloseWindow()
|
||||
// and then onBeforeShutdown(). Using the quit action however will first issue onBeforeShutdown()
|
||||
// and then onBeforeWindowClose().
|
||||
// and then onBeforeCloseWindow().
|
||||
//
|
||||
// Here is the behavior on different OS depending on action taken (Electron 1.7.x):
|
||||
//
|
||||
@@ -114,27 +113,27 @@ export class WindowsStateHandler extends Disposable {
|
||||
// - close(1): close one window via the window close button
|
||||
// - closeAll: close all windows via the taskbar command
|
||||
// - onBeforeShutdown(N): number of windows reported in this event handler
|
||||
// - onBeforeWindowClose(N, M): number of windows reported and quitRequested boolean in this event handler
|
||||
// - onBeforeCloseWindow(N, M): number of windows reported and quitRequested boolean in this event handler
|
||||
//
|
||||
// macOS
|
||||
// - quit(1): onBeforeShutdown(1), onBeforeWindowClose(1, true)
|
||||
// - quit(2): onBeforeShutdown(2), onBeforeWindowClose(2, true), onBeforeWindowClose(2, true)
|
||||
// - quit(1): onBeforeShutdown(1), onBeforeCloseWindow(1, true)
|
||||
// - quit(2): onBeforeShutdown(2), onBeforeCloseWindow(2, true), onBeforeCloseWindow(2, true)
|
||||
// - quit(0): onBeforeShutdown(0)
|
||||
// - close(1): onBeforeWindowClose(1, false)
|
||||
// - close(1): onBeforeCloseWindow(1, false)
|
||||
//
|
||||
// Windows
|
||||
// - quit(1): onBeforeShutdown(1), onBeforeWindowClose(1, true)
|
||||
// - quit(2): onBeforeShutdown(2), onBeforeWindowClose(2, true), onBeforeWindowClose(2, true)
|
||||
// - close(1): onBeforeWindowClose(2, false)[not last window]
|
||||
// - close(1): onBeforeWindowClose(1, false), onBeforeShutdown(0)[last window]
|
||||
// - closeAll(2): onBeforeWindowClose(2, false), onBeforeWindowClose(2, false), onBeforeShutdown(0)
|
||||
// - quit(1): onBeforeShutdown(1), onBeforeCloseWindow(1, true)
|
||||
// - quit(2): onBeforeShutdown(2), onBeforeCloseWindow(2, true), onBeforeCloseWindow(2, true)
|
||||
// - close(1): onBeforeCloseWindow(2, false)[not last window]
|
||||
// - close(1): onBeforeCloseWindow(1, false), onBeforeShutdown(0)[last window]
|
||||
// - closeAll(2): onBeforeCloseWindow(2, false), onBeforeCloseWindow(2, false), onBeforeShutdown(0)
|
||||
//
|
||||
// Linux
|
||||
// - quit(1): onBeforeShutdown(1), onBeforeWindowClose(1, true)
|
||||
// - quit(2): onBeforeShutdown(2), onBeforeWindowClose(2, true), onBeforeWindowClose(2, true)
|
||||
// - close(1): onBeforeWindowClose(2, false)[not last window]
|
||||
// - close(1): onBeforeWindowClose(1, false), onBeforeShutdown(0)[last window]
|
||||
// - closeAll(2): onBeforeWindowClose(2, false), onBeforeWindowClose(2, false), onBeforeShutdown(0)
|
||||
// - quit(1): onBeforeShutdown(1), onBeforeCloseWindow(1, true)
|
||||
// - quit(2): onBeforeShutdown(2), onBeforeCloseWindow(2, true), onBeforeCloseWindow(2, true)
|
||||
// - close(1): onBeforeCloseWindow(2, false)[not last window]
|
||||
// - close(1): onBeforeCloseWindow(1, false), onBeforeShutdown(0)[last window]
|
||||
// - closeAll(2): onBeforeCloseWindow(2, false), onBeforeCloseWindow(2, false), onBeforeShutdown(0)
|
||||
//
|
||||
private onBeforeShutdown(): void {
|
||||
this.shuttingDown = true;
|
||||
@@ -186,7 +185,7 @@ export class WindowsStateHandler extends Disposable {
|
||||
}
|
||||
|
||||
// See note on #onBeforeShutdown() for details how these events are flowing
|
||||
private onBeforeWindowClose(window: ICodeWindow): void {
|
||||
private onBeforeCloseWindow(window: ICodeWindow): void {
|
||||
if (this.lifecycleMainService.quitRequested) {
|
||||
return; // during quit, many windows close in parallel so let it be handled in the before-quit handler
|
||||
}
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
import * as assert from 'assert';
|
||||
import { join } from 'vs/base/common/path';
|
||||
import { findWindowOnFile } from 'vs/platform/windows/electron-main/windowsFinder';
|
||||
import { ICodeWindow, IWindowState } from 'vs/platform/windows/electron-main/windows';
|
||||
import { ICodeWindow, ILoadEvent, IWindowState } from 'vs/platform/windows/electron-main/windows';
|
||||
import { IWorkspaceIdentifier, toWorkspaceFolders } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { getPathFromAmdModule } from 'vs/base/common/amd';
|
||||
import { getPathFromAmdModule } from 'vs/base/test/node/testUtils';
|
||||
import { extUriBiasedIgnorePathCase } from 'vs/base/common/resources';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
@@ -32,13 +32,13 @@ suite('WindowsFinder', () => {
|
||||
|
||||
function createTestCodeWindow(options: { lastFocusTime: number, openedFolderUri?: URI, openedWorkspace?: IWorkspaceIdentifier }): ICodeWindow {
|
||||
return new class implements ICodeWindow {
|
||||
onLoad: Event<void> = Event.None;
|
||||
onReady: Event<void> = Event.None;
|
||||
onClose: Event<void> = Event.None;
|
||||
onDestroy: Event<void> = Event.None;
|
||||
onWillLoad: Event<ILoadEvent> = Event.None;
|
||||
onDidSignalReady: Event<void> = Event.None;
|
||||
onDidClose: Event<void> = Event.None;
|
||||
onDidDestroy: Event<void> = Event.None;
|
||||
whenClosedOrLoaded: Promise<void> = Promise.resolve();
|
||||
id: number = -1;
|
||||
win: Electron.BrowserWindow = undefined!;
|
||||
win: Electron.BrowserWindow = null!;
|
||||
config: INativeWindowConfiguration | undefined;
|
||||
openedWorkspace = options.openedFolderUri ? { id: '', uri: options.openedFolderUri } : options.openedWorkspace;
|
||||
backupPath?: string | undefined;
|
||||
Reference in New Issue
Block a user