mirror of
https://github.com/coder/code-server.git
synced 2026-06-21 09:27:11 +02:00
Update to VS Code 1.52.1
This commit is contained in:
@@ -4,7 +4,6 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { isMacintosh, isLinux, isWeb, IProcessEnvironment } from 'vs/base/common/platform';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
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';
|
||||
@@ -85,8 +84,8 @@ export function isFileToOpen(uriToOpen: IWindowOpenable): uriToOpen is IFileToOp
|
||||
|
||||
export type MenuBarVisibility = 'default' | 'visible' | 'toggle' | 'hidden' | 'compact';
|
||||
|
||||
export function getMenuBarVisibility(configurationService: IConfigurationService, environment: IEnvironmentService, isExtensionDevelopment = environment.isExtensionDevelopment): MenuBarVisibility {
|
||||
const titleBarStyle = getTitleBarStyle(configurationService, environment, isExtensionDevelopment);
|
||||
export function getMenuBarVisibility(configurationService: IConfigurationService): MenuBarVisibility {
|
||||
const titleBarStyle = getTitleBarStyle(configurationService);
|
||||
const menuBarVisibility = configurationService.getValue<MenuBarVisibility>('window.menuBarVisibility');
|
||||
|
||||
if (titleBarStyle === 'native' && menuBarVisibility === 'compact') {
|
||||
@@ -104,7 +103,7 @@ export interface IWindowSettings {
|
||||
openFilesInNewWindow: 'on' | 'off' | 'default';
|
||||
openFoldersInNewWindow: 'on' | 'off' | 'default';
|
||||
openWithoutArgumentsInNewWindow: 'on' | 'off';
|
||||
restoreWindows: 'all' | 'folders' | 'one' | 'none';
|
||||
restoreWindows: 'preserve' | 'all' | 'folders' | 'one' | 'none';
|
||||
restoreFullscreen: boolean;
|
||||
zoomLevel: number;
|
||||
titleBarStyle: 'native' | 'custom';
|
||||
@@ -119,17 +118,12 @@ export interface IWindowSettings {
|
||||
enableExperimentalProxyLoginDialog: boolean;
|
||||
}
|
||||
|
||||
export function getTitleBarStyle(configurationService: IConfigurationService, environment: IEnvironmentService, isExtensionDevelopment = environment.isExtensionDevelopment): 'native' | 'custom' {
|
||||
export function getTitleBarStyle(configurationService: IConfigurationService): 'native' | 'custom' {
|
||||
if (isWeb) {
|
||||
return 'custom';
|
||||
}
|
||||
|
||||
const configuration = configurationService.getValue<IWindowSettings>('window');
|
||||
|
||||
const isDev = !environment.isBuilt || isExtensionDevelopment;
|
||||
if (isMacintosh && isDev) {
|
||||
return 'native'; // not enabled when developing due to https://github.com/electron/electron/issues/3647
|
||||
}
|
||||
const configuration = configurationService.getValue<IWindowSettings | undefined>('window');
|
||||
|
||||
if (configuration) {
|
||||
const useNativeTabs = isMacintosh && configuration.nativeTabs === true;
|
||||
@@ -230,6 +224,10 @@ export interface IWindowConfiguration {
|
||||
filesToDiff?: IPath[];
|
||||
}
|
||||
|
||||
export interface IOSConfiguration {
|
||||
release: string;
|
||||
}
|
||||
|
||||
export interface INativeWindowConfiguration extends IWindowConfiguration, NativeParsedArgs {
|
||||
mainPid: number;
|
||||
|
||||
@@ -256,6 +254,8 @@ export interface INativeWindowConfiguration extends IWindowConfiguration, Native
|
||||
|
||||
userEnv: IProcessEnvironment;
|
||||
filesToWait?: IPathsToWaitFor;
|
||||
|
||||
os: IOSConfiguration;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,8 +12,9 @@ import { IProcessEnvironment } from 'vs/base/common/platform';
|
||||
import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { ISerializableCommandAction } from 'vs/platform/actions/common/actions';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { Rectangle, BrowserWindow } from 'electron';
|
||||
import { Rectangle, BrowserWindow, WebContents } from 'electron';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
|
||||
export interface IWindowState {
|
||||
width?: number;
|
||||
@@ -33,6 +34,11 @@ export const enum WindowMode {
|
||||
|
||||
export interface ICodeWindow extends IDisposable {
|
||||
|
||||
readonly onLoad: Event<void>;
|
||||
readonly onReady: Event<void>;
|
||||
readonly onClose: Event<void>;
|
||||
readonly onDestroy: Event<void>;
|
||||
|
||||
readonly whenClosedOrLoaded: Promise<void>;
|
||||
|
||||
readonly id: number;
|
||||
@@ -59,7 +65,7 @@ export interface ICodeWindow extends IDisposable {
|
||||
addTabbedWindow(window: ICodeWindow): void;
|
||||
|
||||
load(config: INativeWindowConfiguration, isReload?: boolean): void;
|
||||
reload(configuration?: INativeWindowConfiguration, cli?: NativeParsedArgs): void;
|
||||
reload(cli?: NativeParsedArgs): void;
|
||||
|
||||
focus(options?: { force: boolean }): void;
|
||||
close(): void;
|
||||
@@ -67,7 +73,7 @@ export interface ICodeWindow extends IDisposable {
|
||||
getBounds(): Rectangle;
|
||||
|
||||
send(channel: string, ...args: any[]): void;
|
||||
sendWhenReady(channel: string, ...args: any[]): void;
|
||||
sendWhenReady(channel: string, token: CancellationToken, ...args: any[]): void;
|
||||
|
||||
readonly isFullScreen: boolean;
|
||||
toggleFullScreen(): void;
|
||||
@@ -113,6 +119,7 @@ export interface IWindowsMainService {
|
||||
getLastActiveWindow(): ICodeWindow | undefined;
|
||||
|
||||
getWindowById(windowId: number): ICodeWindow | undefined;
|
||||
getWindowByWebContents(webContents: WebContents): ICodeWindow | undefined;
|
||||
getWindows(): ICodeWindow[];
|
||||
getWindowCount(): number;
|
||||
}
|
||||
|
||||
@@ -3,18 +3,17 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as fs from 'fs';
|
||||
import { statSync, unlink } from 'fs';
|
||||
import { basename, normalize, join, posix } from 'vs/base/common/path';
|
||||
import { localize } from 'vs/nls';
|
||||
import * as arrays from 'vs/base/common/arrays';
|
||||
import { mixin } from 'vs/base/common/objects';
|
||||
import { coalesce, distinct, firstOrDefault } from 'vs/base/common/arrays';
|
||||
import { IBackupMainService } from 'vs/platform/backup/electron-main/backup';
|
||||
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, defaultWindowState } from 'vs/code/electron-main/window';
|
||||
import { screen, BrowserWindow, MessageBoxOptions, Display, app } from 'electron';
|
||||
import { screen, BrowserWindow, MessageBoxOptions, Display, app, WebContents } from 'electron';
|
||||
import { ILifecycleMainService, UnloadReason, LifecycleMainService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
@@ -40,6 +39,7 @@ import { withNullAsUndefined } from 'vs/base/common/types';
|
||||
import { isWindowsDriveLetter, toSlashes, parseLineAndColumnAware } 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';
|
||||
|
||||
export interface IWindowState {
|
||||
workspace?: IWorkspaceIdentifier;
|
||||
@@ -59,7 +59,7 @@ interface INewWindowState extends ISingleWindowState {
|
||||
hasDefaultState?: boolean;
|
||||
}
|
||||
|
||||
type RestoreWindowsSetting = 'all' | 'folders' | 'one' | 'none';
|
||||
type RestoreWindowsSetting = 'preserve' | 'all' | 'folders' | 'one' | 'none';
|
||||
|
||||
interface IOpenBrowserWindowOptions {
|
||||
userEnv?: IProcessEnvironment;
|
||||
@@ -72,7 +72,7 @@ interface IOpenBrowserWindowOptions {
|
||||
|
||||
initialStartup?: boolean;
|
||||
|
||||
fileInputs?: IFileInputs;
|
||||
filesToOpen?: IFilesToOpen;
|
||||
|
||||
forceNewWindow?: boolean;
|
||||
forceNewTabbedWindow?: boolean;
|
||||
@@ -87,7 +87,7 @@ interface IPathParseOptions {
|
||||
remoteAuthority?: string;
|
||||
}
|
||||
|
||||
interface IFileInputs {
|
||||
interface IFilesToOpen {
|
||||
filesToOpenOrCreate: IPath[];
|
||||
filesToDiff: IPath[];
|
||||
filesToWait?: IPathsToWaitFor;
|
||||
@@ -307,7 +307,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
currentWindowsState.lastPluginDevelopmentHostWindow = this.toWindowState(extensionHostWindow);
|
||||
}
|
||||
|
||||
// 3.) All windows (except extension host) for N >= 2 to support restoreWindows: all or for auto update
|
||||
// 3.) All windows (except extension host) for N >= 2 to support `restoreWindows: all` or for auto update
|
||||
//
|
||||
// Careful here: asking a window for its window state after it has been closed returns bogus values (width: 0, height: 0)
|
||||
// so if we ever want to persist the UI state of the last closed window (window count === 1), it has
|
||||
@@ -385,17 +385,17 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
this.logService.trace('windowsManager#open');
|
||||
openConfig = this.validateOpenConfig(openConfig);
|
||||
|
||||
const pathsToOpen = this.getPathsToOpen(openConfig);
|
||||
this.logService.trace('windowsManager#open pathsToOpen', pathsToOpen);
|
||||
|
||||
const foldersToAdd: IFolderPathToOpen[] = [];
|
||||
const foldersToOpen: IFolderPathToOpen[] = [];
|
||||
const workspacesToOpen: IWorkspacePathToOpen[] = [];
|
||||
const workspacesToRestore: IWorkspacePathToOpen[] = [];
|
||||
const emptyToRestore: IEmptyWindowBackupInfo[] = []; // empty windows with backupPath
|
||||
const emptyToRestore: IEmptyWindowBackupInfo[] = [];
|
||||
let filesToOpen: IFilesToOpen | undefined;
|
||||
let emptyToOpen = 0;
|
||||
|
||||
let emptyToOpen: number = 0;
|
||||
let fileInputs: IFileInputs | undefined; // collect all file inputs
|
||||
// Identify things to open from open config
|
||||
const pathsToOpen = this.getPathsToOpen(openConfig);
|
||||
this.logService.trace('windowsManager#open pathsToOpen', pathsToOpen);
|
||||
for (const path of pathsToOpen) {
|
||||
if (isFolderPathToOpen(path)) {
|
||||
if (openConfig.addMode) {
|
||||
@@ -408,10 +408,10 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
} else if (isWorkspacePathToOpen(path)) {
|
||||
workspacesToOpen.push(path);
|
||||
} else if (path.fileUri) {
|
||||
if (!fileInputs) {
|
||||
fileInputs = { filesToOpenOrCreate: [], filesToDiff: [], remoteAuthority: path.remoteAuthority };
|
||||
if (!filesToOpen) {
|
||||
filesToOpen = { filesToOpenOrCreate: [], filesToDiff: [], remoteAuthority: path.remoteAuthority };
|
||||
}
|
||||
fileInputs.filesToOpenOrCreate.push(path);
|
||||
filesToOpen.filesToOpenOrCreate.push(path);
|
||||
} else if (path.backupPath) {
|
||||
emptyToRestore.push({ backupFolder: basename(path.backupPath), remoteAuthority: path.remoteAuthority });
|
||||
} else {
|
||||
@@ -421,14 +421,14 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
|
||||
// When run with --diff, take the files to open as files to diff
|
||||
// if there are exactly two files provided.
|
||||
if (fileInputs && openConfig.diffMode && fileInputs.filesToOpenOrCreate.length === 2) {
|
||||
fileInputs.filesToDiff = fileInputs.filesToOpenOrCreate;
|
||||
fileInputs.filesToOpenOrCreate = [];
|
||||
if (openConfig.diffMode && filesToOpen?.filesToOpenOrCreate.length === 2) {
|
||||
filesToOpen.filesToDiff = filesToOpen.filesToOpenOrCreate;
|
||||
filesToOpen.filesToOpenOrCreate = [];
|
||||
}
|
||||
|
||||
// When run with --wait, make sure we keep the paths to wait for
|
||||
if (fileInputs && openConfig.waitMarkerFileURI) {
|
||||
fileInputs.filesToWait = { paths: [...fileInputs.filesToDiff, ...fileInputs.filesToOpenOrCreate], waitMarkerFileUri: openConfig.waitMarkerFileURI };
|
||||
if (filesToOpen && openConfig.waitMarkerFileURI) {
|
||||
filesToOpen.filesToWait = { paths: [...filesToOpen.filesToDiff, ...filesToOpen.filesToOpenOrCreate], waitMarkerFileUri: openConfig.waitMarkerFileURI };
|
||||
}
|
||||
|
||||
//
|
||||
@@ -447,52 +447,61 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
}
|
||||
|
||||
// Open based on config
|
||||
const usedWindows = this.doOpen(openConfig, workspacesToOpen, foldersToOpen, emptyToRestore, emptyToOpen, fileInputs, foldersToAdd);
|
||||
const { windows: usedWindows, filesOpenedInWindow } = this.doOpen(openConfig, workspacesToOpen, foldersToOpen, emptyToRestore, emptyToOpen, filesToOpen, foldersToAdd);
|
||||
|
||||
this.logService.trace(`windowsManager#open used window count ${usedWindows.length} (workspacesToOpen: ${workspacesToOpen.length}, foldersToOpen: ${foldersToOpen.length}, emptyToRestore: ${emptyToRestore.length}, emptyToOpen: ${emptyToOpen})`);
|
||||
|
||||
// Make sure to pass focus to the most relevant of the windows if we open multiple
|
||||
if (usedWindows.length > 1) {
|
||||
const focusLastActive = this.windowsState.lastActiveWindow && !openConfig.forceEmpty && !openConfig.cli._.length && !openConfig.cli['file-uri'] && !openConfig.cli['folder-uri'] && !(openConfig.urisToOpen && openConfig.urisToOpen.length);
|
||||
let focusLastOpened = true;
|
||||
let focusLastWindow = true;
|
||||
|
||||
// 1.) focus last active window if we are not instructed to open any paths
|
||||
if (focusLastActive) {
|
||||
const lastActiveWindow = usedWindows.filter(window => this.windowsState.lastActiveWindow && window.backupPath === this.windowsState.lastActiveWindow.backupPath);
|
||||
if (lastActiveWindow.length) {
|
||||
lastActiveWindow[0].focus();
|
||||
focusLastOpened = false;
|
||||
focusLastWindow = false;
|
||||
}
|
||||
// 1.) focus window we opened files in always with highest priority
|
||||
if (filesOpenedInWindow) {
|
||||
filesOpenedInWindow.focus();
|
||||
}
|
||||
|
||||
// 2.) if instructed to open paths, focus last window which is not restored
|
||||
if (focusLastOpened) {
|
||||
for (let i = usedWindows.length - 1; i >= 0; i--) {
|
||||
const usedWindow = usedWindows[i];
|
||||
if (
|
||||
(usedWindow.openedWorkspace && workspacesToRestore.some(workspace => usedWindow.openedWorkspace && workspace.workspace.id === usedWindow.openedWorkspace.id)) || // skip over restored workspace
|
||||
(usedWindow.backupPath && emptyToRestore.some(empty => usedWindow.backupPath && empty.backupFolder === basename(usedWindow.backupPath))) // skip over restored empty window
|
||||
) {
|
||||
continue;
|
||||
// Otherwise, find a good window based on open params
|
||||
else {
|
||||
const focusLastActive = this.windowsState.lastActiveWindow && !openConfig.forceEmpty && !openConfig.cli._.length && !openConfig.cli['file-uri'] && !openConfig.cli['folder-uri'] && !(openConfig.urisToOpen && openConfig.urisToOpen.length);
|
||||
let focusLastOpened = true;
|
||||
let focusLastWindow = true;
|
||||
|
||||
// 2.) focus last active window if we are not instructed to open any paths
|
||||
if (focusLastActive) {
|
||||
const lastActiveWindow = usedWindows.filter(window => this.windowsState.lastActiveWindow && window.backupPath === this.windowsState.lastActiveWindow.backupPath);
|
||||
if (lastActiveWindow.length) {
|
||||
lastActiveWindow[0].focus();
|
||||
focusLastOpened = false;
|
||||
focusLastWindow = false;
|
||||
}
|
||||
|
||||
usedWindow.focus();
|
||||
focusLastWindow = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 3.) finally, always ensure to have at least last used window focused
|
||||
if (focusLastWindow) {
|
||||
usedWindows[usedWindows.length - 1].focus();
|
||||
// 3.) if instructed to open paths, focus last window which is not restored
|
||||
if (focusLastOpened) {
|
||||
for (let i = usedWindows.length - 1; i >= 0; i--) {
|
||||
const usedWindow = usedWindows[i];
|
||||
if (
|
||||
(usedWindow.openedWorkspace && workspacesToRestore.some(workspace => usedWindow.openedWorkspace && workspace.workspace.id === usedWindow.openedWorkspace.id)) || // skip over restored workspace
|
||||
(usedWindow.backupPath && emptyToRestore.some(empty => usedWindow.backupPath && empty.backupFolder === basename(usedWindow.backupPath))) // skip over restored empty window
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
usedWindow.focus();
|
||||
focusLastWindow = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 4.) finally, always ensure to have at least last used window focused
|
||||
if (focusLastWindow) {
|
||||
usedWindows[usedWindows.length - 1].focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remember in recent document list (unless this opens for extension development)
|
||||
// Also do not add paths when files are opened for diffing, only if opened individually
|
||||
const isDiff = fileInputs && fileInputs.filesToDiff.length > 0;
|
||||
const isDiff = filesToOpen && filesToOpen.filesToDiff.length > 0;
|
||||
if (!usedWindows.some(window => window.isExtensionDevelopmentHost) && !isDiff && !openConfig.noRecentEntry) {
|
||||
const recents: IRecent[] = [];
|
||||
for (let pathToOpen of pathsToOpen) {
|
||||
@@ -513,7 +522,7 @@ 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(() => fs.unlink(waitMarkerFileURI.fsPath, _error => undefined));
|
||||
usedWindows[0].whenClosedOrLoaded.then(() => unlink(waitMarkerFileURI.fsPath, () => undefined));
|
||||
}
|
||||
|
||||
return usedWindows;
|
||||
@@ -535,10 +544,11 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
foldersToOpen: IFolderPathToOpen[],
|
||||
emptyToRestore: IEmptyWindowBackupInfo[],
|
||||
emptyToOpen: number,
|
||||
fileInputs: IFileInputs | undefined,
|
||||
filesToOpen: IFilesToOpen | undefined,
|
||||
foldersToAdd: IFolderPathToOpen[]
|
||||
) {
|
||||
): { windows: ICodeWindow[], filesOpenedInWindow: ICodeWindow | undefined } {
|
||||
const usedWindows: ICodeWindow[] = [];
|
||||
let filesOpenedInWindow: ICodeWindow | undefined = undefined;
|
||||
|
||||
// Settings can decide if files/folders open in new window or not
|
||||
let { openFolderInNewWindow, openFilesInNewWindow } = this.shouldOpenNewWindow(openConfig);
|
||||
@@ -554,13 +564,13 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
|
||||
// Handle files to open/diff or to create when we dont open a folder and we do not restore any folder/untitled from hot-exit
|
||||
const potentialWindowsCount = foldersToOpen.length + workspacesToOpen.length + emptyToRestore.length;
|
||||
if (potentialWindowsCount === 0 && fileInputs) {
|
||||
if (potentialWindowsCount === 0 && filesToOpen) {
|
||||
|
||||
// Find suitable window or folder path to open files in
|
||||
const fileToCheck = fileInputs.filesToOpenOrCreate[0] || fileInputs.filesToDiff[0];
|
||||
const fileToCheck = filesToOpen.filesToOpenOrCreate[0] || filesToOpen.filesToDiff[0];
|
||||
|
||||
// only look at the windows with correct authority
|
||||
const windows = WindowsMainService.WINDOWS.filter(window => fileInputs && window.remoteAuthority === fileInputs.remoteAuthority);
|
||||
const windows = WindowsMainService.WINDOWS.filter(window => filesToOpen && window.remoteAuthority === filesToOpen.remoteAuthority);
|
||||
|
||||
const bestWindowOrFolder = findBestWindowOrFolderForFile({
|
||||
windows,
|
||||
@@ -587,46 +597,52 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
else {
|
||||
|
||||
// Do open files
|
||||
usedWindows.push(this.doOpenFilesInExistingWindow(openConfig, bestWindowOrFolder, fileInputs));
|
||||
const window = this.doOpenFilesInExistingWindow(openConfig, bestWindowOrFolder, filesToOpen);
|
||||
usedWindows.push(window);
|
||||
|
||||
// Reset these because we handled them
|
||||
fileInputs = undefined;
|
||||
// Reset `filesToOpen` because we handled them and also remember window we used
|
||||
filesToOpen = undefined;
|
||||
filesOpenedInWindow = window;
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, if no window or folder is found, just open the files in an empty window
|
||||
else {
|
||||
usedWindows.push(this.openInBrowserWindow({
|
||||
const window = this.openInBrowserWindow({
|
||||
userEnv: openConfig.userEnv,
|
||||
cli: openConfig.cli,
|
||||
initialStartup: openConfig.initialStartup,
|
||||
fileInputs,
|
||||
filesToOpen,
|
||||
forceNewWindow: true,
|
||||
remoteAuthority: fileInputs.remoteAuthority,
|
||||
remoteAuthority: filesToOpen.remoteAuthority,
|
||||
forceNewTabbedWindow: openConfig.forceNewTabbedWindow
|
||||
}));
|
||||
});
|
||||
usedWindows.push(window);
|
||||
|
||||
// Reset these because we handled them
|
||||
fileInputs = undefined;
|
||||
// Reset `filesToOpen` because we handled them and also remember window we used
|
||||
filesToOpen = undefined;
|
||||
filesOpenedInWindow = window;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle workspaces to open (instructed and to restore)
|
||||
const allWorkspacesToOpen = arrays.distinct(workspacesToOpen, workspace => workspace.workspace.id); // prevent duplicates
|
||||
const allWorkspacesToOpen = distinct(workspacesToOpen, workspace => workspace.workspace.id); // prevent duplicates
|
||||
if (allWorkspacesToOpen.length > 0) {
|
||||
|
||||
// Check for existing instances
|
||||
const windowsOnWorkspace = arrays.coalesce(allWorkspacesToOpen.map(workspaceToOpen => findWindowOnWorkspace(WindowsMainService.WINDOWS, workspaceToOpen.workspace)));
|
||||
const windowsOnWorkspace = coalesce(allWorkspacesToOpen.map(workspaceToOpen => findWindowOnWorkspace(WindowsMainService.WINDOWS, workspaceToOpen.workspace)));
|
||||
if (windowsOnWorkspace.length > 0) {
|
||||
const windowOnWorkspace = windowsOnWorkspace[0];
|
||||
const fileInputsForWindow = (fileInputs?.remoteAuthority === windowOnWorkspace.remoteAuthority) ? fileInputs : undefined;
|
||||
const filesToOpenInWindow = (filesToOpen?.remoteAuthority === windowOnWorkspace.remoteAuthority) ? filesToOpen : undefined;
|
||||
|
||||
// Do open files
|
||||
usedWindows.push(this.doOpenFilesInExistingWindow(openConfig, windowOnWorkspace, fileInputsForWindow));
|
||||
const window = this.doOpenFilesInExistingWindow(openConfig, windowOnWorkspace, filesToOpenInWindow);
|
||||
usedWindows.push(window);
|
||||
|
||||
// Reset these because we handled them
|
||||
if (fileInputsForWindow) {
|
||||
fileInputs = undefined;
|
||||
// Reset `filesToOpen` because we handled them and also remember window we used
|
||||
if (filesToOpenInWindow) {
|
||||
filesToOpen = undefined;
|
||||
filesOpenedInWindow = window;
|
||||
}
|
||||
|
||||
openFolderInNewWindow = true; // any other folders to open must open in new window then
|
||||
@@ -639,14 +655,16 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
}
|
||||
|
||||
const remoteAuthority = workspaceToOpen.remoteAuthority;
|
||||
const fileInputsForWindow = (fileInputs?.remoteAuthority === remoteAuthority) ? fileInputs : undefined;
|
||||
const filesToOpenInWindow = (filesToOpen?.remoteAuthority === remoteAuthority) ? filesToOpen : undefined;
|
||||
|
||||
// Do open folder
|
||||
usedWindows.push(this.doOpenFolderOrWorkspace(openConfig, workspaceToOpen, openFolderInNewWindow, fileInputsForWindow));
|
||||
const window = this.doOpenFolderOrWorkspace(openConfig, workspaceToOpen, openFolderInNewWindow, filesToOpenInWindow);
|
||||
usedWindows.push(window);
|
||||
|
||||
// Reset these because we handled them
|
||||
if (fileInputsForWindow) {
|
||||
fileInputs = undefined;
|
||||
// Reset `filesToOpen` because we handled them and also remember window we used
|
||||
if (filesToOpenInWindow) {
|
||||
filesToOpen = undefined;
|
||||
filesOpenedInWindow = window;
|
||||
}
|
||||
|
||||
openFolderInNewWindow = true; // any other folders to open must open in new window then
|
||||
@@ -654,21 +672,23 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
}
|
||||
|
||||
// Handle folders to open (instructed and to restore)
|
||||
const allFoldersToOpen = arrays.distinct(foldersToOpen, folder => extUriBiasedIgnorePathCase.getComparisonKey(folder.folderUri)); // prevent duplicates
|
||||
const allFoldersToOpen = distinct(foldersToOpen, folder => extUriBiasedIgnorePathCase.getComparisonKey(folder.folderUri)); // prevent duplicates
|
||||
if (allFoldersToOpen.length > 0) {
|
||||
|
||||
// Check for existing instances
|
||||
const windowsOnFolderPath = arrays.coalesce(allFoldersToOpen.map(folderToOpen => findWindowOnWorkspace(WindowsMainService.WINDOWS, folderToOpen.folderUri)));
|
||||
const windowsOnFolderPath = coalesce(allFoldersToOpen.map(folderToOpen => findWindowOnWorkspace(WindowsMainService.WINDOWS, folderToOpen.folderUri)));
|
||||
if (windowsOnFolderPath.length > 0) {
|
||||
const windowOnFolderPath = windowsOnFolderPath[0];
|
||||
const fileInputsForWindow = fileInputs?.remoteAuthority === windowOnFolderPath.remoteAuthority ? fileInputs : undefined;
|
||||
const filesToOpenInWindow = filesToOpen?.remoteAuthority === windowOnFolderPath.remoteAuthority ? filesToOpen : undefined;
|
||||
|
||||
// Do open files
|
||||
usedWindows.push(this.doOpenFilesInExistingWindow(openConfig, windowOnFolderPath, fileInputsForWindow));
|
||||
const window = this.doOpenFilesInExistingWindow(openConfig, windowOnFolderPath, filesToOpenInWindow);
|
||||
usedWindows.push(window);
|
||||
|
||||
// Reset these because we handled them
|
||||
if (fileInputsForWindow) {
|
||||
fileInputs = undefined;
|
||||
// Reset `filesToOpen` because we handled them and also remember window we used
|
||||
if (filesToOpenInWindow) {
|
||||
filesToOpen = undefined;
|
||||
filesOpenedInWindow = window;
|
||||
}
|
||||
|
||||
openFolderInNewWindow = true; // any other folders to open must open in new window then
|
||||
@@ -682,14 +702,16 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
}
|
||||
|
||||
const remoteAuthority = folderToOpen.remoteAuthority;
|
||||
const fileInputsForWindow = (fileInputs?.remoteAuthority === remoteAuthority) ? fileInputs : undefined;
|
||||
const filesToOpenInWindow = (filesToOpen?.remoteAuthority === remoteAuthority) ? filesToOpen : undefined;
|
||||
|
||||
// Do open folder
|
||||
usedWindows.push(this.doOpenFolderOrWorkspace(openConfig, folderToOpen, openFolderInNewWindow, fileInputsForWindow));
|
||||
const window = this.doOpenFolderOrWorkspace(openConfig, folderToOpen, openFolderInNewWindow, filesToOpenInWindow);
|
||||
usedWindows.push(window);
|
||||
|
||||
// Reset these because we handled them
|
||||
if (fileInputsForWindow) {
|
||||
fileInputs = undefined;
|
||||
// Reset `filesToOpen` because we handled them and also remember window we used
|
||||
if (filesToOpenInWindow) {
|
||||
filesToOpen = undefined;
|
||||
filesOpenedInWindow = window;
|
||||
}
|
||||
|
||||
openFolderInNewWindow = true; // any other folders to open must open in new window then
|
||||
@@ -697,26 +719,28 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
}
|
||||
|
||||
// Handle empty to restore
|
||||
const allEmptyToRestore = arrays.distinct(emptyToRestore, info => info.backupFolder); // prevent duplicates
|
||||
const allEmptyToRestore = distinct(emptyToRestore, info => info.backupFolder); // prevent duplicates
|
||||
if (allEmptyToRestore.length > 0) {
|
||||
allEmptyToRestore.forEach(emptyWindowBackupInfo => {
|
||||
const remoteAuthority = emptyWindowBackupInfo.remoteAuthority;
|
||||
const fileInputsForWindow = (fileInputs?.remoteAuthority === remoteAuthority) ? fileInputs : undefined;
|
||||
const filesToOpenInWindow = (filesToOpen?.remoteAuthority === remoteAuthority) ? filesToOpen : undefined;
|
||||
|
||||
usedWindows.push(this.openInBrowserWindow({
|
||||
const window = this.openInBrowserWindow({
|
||||
userEnv: openConfig.userEnv,
|
||||
cli: openConfig.cli,
|
||||
initialStartup: openConfig.initialStartup,
|
||||
fileInputs: fileInputsForWindow,
|
||||
filesToOpen: filesToOpenInWindow,
|
||||
remoteAuthority,
|
||||
forceNewWindow: true,
|
||||
forceNewTabbedWindow: openConfig.forceNewTabbedWindow,
|
||||
emptyWindowBackupInfo
|
||||
}));
|
||||
});
|
||||
usedWindows.push(window);
|
||||
|
||||
// Reset these because we handled them
|
||||
if (fileInputsForWindow) {
|
||||
fileInputs = undefined;
|
||||
// Reset `filesToOpen` because we handled them and also remember window we used
|
||||
if (filesToOpenInWindow) {
|
||||
filesToOpen = undefined;
|
||||
filesOpenedInWindow = window;
|
||||
}
|
||||
|
||||
openFolderInNewWindow = true; // any other folders to open must open in new window then
|
||||
@@ -724,42 +748,48 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
}
|
||||
|
||||
// Handle empty to open (only if no other window opened)
|
||||
if (usedWindows.length === 0 || fileInputs) {
|
||||
if (fileInputs && !emptyToOpen) {
|
||||
if (usedWindows.length === 0 || filesToOpen) {
|
||||
if (filesToOpen && !emptyToOpen) {
|
||||
emptyToOpen++;
|
||||
}
|
||||
|
||||
const remoteAuthority = fileInputs ? fileInputs.remoteAuthority : (openConfig.cli && openConfig.cli.remote || undefined);
|
||||
const remoteAuthority = filesToOpen ? filesToOpen.remoteAuthority : (openConfig.cli && openConfig.cli.remote || undefined);
|
||||
|
||||
for (let i = 0; i < emptyToOpen; i++) {
|
||||
usedWindows.push(this.doOpenEmpty(openConfig, openFolderInNewWindow, remoteAuthority, fileInputs));
|
||||
const window = this.doOpenEmpty(openConfig, openFolderInNewWindow, remoteAuthority, filesToOpen);
|
||||
usedWindows.push(window);
|
||||
|
||||
// Reset these because we handled them
|
||||
fileInputs = undefined;
|
||||
openFolderInNewWindow = true; // any other window to open must open in new window then
|
||||
// Reset `filesToOpen` because we handled them and also remember window we used
|
||||
if (filesToOpen) {
|
||||
filesToOpen = undefined;
|
||||
filesOpenedInWindow = window;
|
||||
}
|
||||
|
||||
// any other window to open must open in new window then
|
||||
openFolderInNewWindow = true;
|
||||
}
|
||||
}
|
||||
|
||||
return arrays.distinct(usedWindows);
|
||||
return { windows: distinct(usedWindows), filesOpenedInWindow };
|
||||
}
|
||||
|
||||
private doOpenFilesInExistingWindow(configuration: IOpenConfiguration, window: ICodeWindow, fileInputs?: IFileInputs): ICodeWindow {
|
||||
private doOpenFilesInExistingWindow(configuration: IOpenConfiguration, window: ICodeWindow, filesToOpen?: IFilesToOpen): ICodeWindow {
|
||||
this.logService.trace('windowsManager#doOpenFilesInExistingWindow');
|
||||
|
||||
window.focus(); // make sure window has focus
|
||||
|
||||
const params: { filesToOpenOrCreate?: IPath[], filesToDiff?: IPath[], filesToWait?: IPathsToWaitFor, termProgram?: string } = {};
|
||||
if (fileInputs) {
|
||||
params.filesToOpenOrCreate = fileInputs.filesToOpenOrCreate;
|
||||
params.filesToDiff = fileInputs.filesToDiff;
|
||||
params.filesToWait = fileInputs.filesToWait;
|
||||
if (filesToOpen) {
|
||||
params.filesToOpenOrCreate = filesToOpen.filesToOpenOrCreate;
|
||||
params.filesToDiff = filesToOpen.filesToDiff;
|
||||
params.filesToWait = filesToOpen.filesToWait;
|
||||
}
|
||||
|
||||
if (configuration.userEnv) {
|
||||
params.termProgram = configuration.userEnv['TERM_PROGRAM'];
|
||||
}
|
||||
|
||||
window.sendWhenReady('vscode:openFiles', params);
|
||||
window.sendWhenReady('vscode:openFiles', CancellationToken.None, params);
|
||||
|
||||
return window;
|
||||
}
|
||||
@@ -768,12 +798,12 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
window.focus(); // make sure window has focus
|
||||
|
||||
const request: IAddFoldersRequest = { foldersToAdd };
|
||||
window.sendWhenReady('vscode:addFolders', request);
|
||||
window.sendWhenReady('vscode:addFolders', CancellationToken.None, request);
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
private doOpenEmpty(openConfig: IOpenConfiguration, forceNewWindow: boolean, remoteAuthority: string | undefined, fileInputs: IFileInputs | undefined, windowToUse?: ICodeWindow): ICodeWindow {
|
||||
private doOpenEmpty(openConfig: IOpenConfiguration, forceNewWindow: boolean, remoteAuthority: string | undefined, filesToOpen: IFilesToOpen | undefined, windowToUse?: ICodeWindow): ICodeWindow {
|
||||
if (!forceNewWindow && !windowToUse && typeof openConfig.contextWindowId === 'number') {
|
||||
windowToUse = this.getWindowById(openConfig.contextWindowId); // fix for https://github.com/microsoft/vscode/issues/97172
|
||||
}
|
||||
@@ -785,12 +815,12 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
remoteAuthority,
|
||||
forceNewWindow,
|
||||
forceNewTabbedWindow: openConfig.forceNewTabbedWindow,
|
||||
fileInputs,
|
||||
filesToOpen,
|
||||
windowToUse
|
||||
});
|
||||
}
|
||||
|
||||
private doOpenFolderOrWorkspace(openConfig: IOpenConfiguration, folderOrWorkspace: IPathToOpen, forceNewWindow: boolean, fileInputs: IFileInputs | undefined, windowToUse?: ICodeWindow): ICodeWindow {
|
||||
private doOpenFolderOrWorkspace(openConfig: IOpenConfiguration, folderOrWorkspace: IPathToOpen, forceNewWindow: boolean, filesToOpen: IFilesToOpen | undefined, windowToUse?: ICodeWindow): ICodeWindow {
|
||||
if (!forceNewWindow && !windowToUse && typeof openConfig.contextWindowId === 'number') {
|
||||
windowToUse = this.getWindowById(openConfig.contextWindowId); // fix for https://github.com/microsoft/vscode/issues/49587
|
||||
}
|
||||
@@ -801,7 +831,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
initialStartup: openConfig.initialStartup,
|
||||
workspace: folderOrWorkspace.workspace,
|
||||
folderUri: folderOrWorkspace.folderUri,
|
||||
fileInputs,
|
||||
filesToOpen,
|
||||
remoteAuthority: folderOrWorkspace.remoteAuthority,
|
||||
forceNewWindow,
|
||||
forceNewTabbedWindow: openConfig.forceNewTabbedWindow,
|
||||
@@ -812,6 +842,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
private getPathsToOpen(openConfig: IOpenConfiguration): IPathToOpen[] {
|
||||
let windowsToOpen: IPathToOpen[];
|
||||
let isCommandLineOrAPICall = false;
|
||||
let restoredWindows = false;
|
||||
|
||||
// Extract paths: from API
|
||||
if (openConfig.urisToOpen && openConfig.urisToOpen.length > 0) {
|
||||
@@ -833,11 +864,12 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
// Extract windows: from previous session
|
||||
else {
|
||||
windowsToOpen = this.doGetWindowsFromLastSession();
|
||||
restoredWindows = true;
|
||||
}
|
||||
|
||||
// Convert multiple folders into workspace (if opened via API or CLI)
|
||||
// This will ensure to open these folders in one window instead of multiple
|
||||
// If we are in addMode, we should not do this because in that case all
|
||||
// If we are in `addMode`, we should not do this because in that case all
|
||||
// folders should be added to the existing window.
|
||||
if (!openConfig.addMode && isCommandLineOrAPICall) {
|
||||
const foldersToOpen = windowsToOpen.filter(path => !!path.folderUri);
|
||||
@@ -853,6 +885,15 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
}
|
||||
}
|
||||
|
||||
// Check for `window.startup` setting to include all windows
|
||||
// from the previous session if this is the initial startup and we have
|
||||
// not restored windows already otherwise.
|
||||
// Use `unshift` to ensure any new window to open comes last
|
||||
// for proper focus treatment.
|
||||
if (openConfig.initialStartup && !restoredWindows && this.configurationService.getValue<IWindowSettings | undefined>('window')?.restoreWindows === 'preserve') {
|
||||
windowsToOpen.unshift(...this.doGetWindowsFromLastSession().filter(window => window.workspace || window.folderUri || window.backupPath));
|
||||
}
|
||||
|
||||
return windowsToOpen;
|
||||
}
|
||||
|
||||
@@ -914,7 +955,6 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// file uris
|
||||
const fileUris = cli['file-uri'];
|
||||
if (fileUris) {
|
||||
@@ -947,9 +987,9 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
}
|
||||
|
||||
private doGetWindowsFromLastSession(): IPathToOpen[] {
|
||||
const restoreWindows = this.getRestoreWindowsSetting();
|
||||
const restoreWindowsSetting = this.getRestoreWindowsSetting();
|
||||
|
||||
switch (restoreWindows) {
|
||||
switch (restoreWindowsSetting) {
|
||||
|
||||
// none: we always open an empty window
|
||||
case 'none':
|
||||
@@ -960,9 +1000,12 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
// folders: restore last opened folders only
|
||||
case 'one':
|
||||
case 'all':
|
||||
case 'preserve':
|
||||
case 'folders':
|
||||
|
||||
// Collect previously opened windows
|
||||
const openedWindows: IWindowState[] = [];
|
||||
if (restoreWindows !== 'one') {
|
||||
if (restoreWindowsSetting !== 'one') {
|
||||
openedWindows.push(...this.windowsState.openedWindows);
|
||||
}
|
||||
if (this.windowsState.lastActiveWindow) {
|
||||
@@ -971,17 +1014,25 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
|
||||
const windowsToOpen: IPathToOpen[] = [];
|
||||
for (const openedWindow of openedWindows) {
|
||||
if (openedWindow.workspace) { // Workspaces
|
||||
|
||||
// Workspaces
|
||||
if (openedWindow.workspace) {
|
||||
const pathToOpen = this.parseUri({ workspaceUri: openedWindow.workspace.configPath }, { remoteAuthority: openedWindow.remoteAuthority });
|
||||
if (pathToOpen?.workspace) {
|
||||
windowsToOpen.push(pathToOpen);
|
||||
}
|
||||
} else if (openedWindow.folderUri) { // Folders
|
||||
}
|
||||
|
||||
// Folders
|
||||
else if (openedWindow.folderUri) {
|
||||
const pathToOpen = this.parseUri({ folderUri: openedWindow.folderUri }, { remoteAuthority: openedWindow.remoteAuthority });
|
||||
if (pathToOpen?.folderUri) {
|
||||
windowsToOpen.push(pathToOpen);
|
||||
}
|
||||
} else if (restoreWindows !== 'folders' && openedWindow.backupPath) { // Empty window, potentially editors open to be restored
|
||||
}
|
||||
|
||||
// Empty window, potentially editors open to be restored
|
||||
else if (restoreWindowsSetting !== 'folders' && openedWindow.backupPath) {
|
||||
windowsToOpen.push({ backupPath: openedWindow.backupPath, remoteAuthority: openedWindow.remoteAuthority });
|
||||
}
|
||||
}
|
||||
@@ -1002,10 +1053,10 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
if (this.lifecycleMainService.wasRestarted) {
|
||||
restoreWindows = 'all'; // always reopen all windows when an update was applied
|
||||
} else {
|
||||
const windowConfig = this.configurationService.getValue<IWindowSettings>('window');
|
||||
const windowConfig = this.configurationService.getValue<IWindowSettings | undefined>('window');
|
||||
restoreWindows = windowConfig?.restoreWindows || 'all'; // by default restore all windows
|
||||
|
||||
if (!['all', 'folders', 'one', 'none'].includes(restoreWindows)) {
|
||||
if (!['preserve', 'all', 'folders', 'one', 'none'].includes(restoreWindows)) {
|
||||
restoreWindows = 'all'; // by default restore all windows
|
||||
}
|
||||
}
|
||||
@@ -1142,7 +1193,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
let candidate = normalize(anyPath);
|
||||
|
||||
try {
|
||||
const candidateStat = fs.statSync(candidate);
|
||||
const candidateStat = statSync(candidate);
|
||||
if (candidateStat.isFile()) {
|
||||
|
||||
// Workspace (unless disabled via flag)
|
||||
@@ -1197,7 +1248,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
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
|
||||
const windowConfig = this.configurationService.getValue<IWindowSettings>('window');
|
||||
const windowConfig = this.configurationService.getValue<IWindowSettings | undefined>('window');
|
||||
const openFolderInNewWindowConfig = windowConfig?.openFoldersInNewWindow || 'default' /* default */;
|
||||
const openFilesInNewWindowConfig = windowConfig?.openFilesInNewWindow || 'off' /* default */;
|
||||
|
||||
@@ -1348,7 +1399,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
private openInBrowserWindow(options: IOpenBrowserWindowOptions): ICodeWindow {
|
||||
|
||||
// Build INativeWindowConfiguration from config and options
|
||||
const configuration: INativeWindowConfiguration = mixin({}, options.cli); // inherit all properties from CLI
|
||||
const configuration = { ...options.cli } as INativeWindowConfiguration;
|
||||
configuration.appRoot = this.environmentService.appRoot;
|
||||
configuration.machineId = this.machineId;
|
||||
configuration.nodeCachedDataDir = this.environmentService.nodeCachedDataDir;
|
||||
@@ -1360,11 +1411,11 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
configuration.folderUri = options.folderUri;
|
||||
configuration.remoteAuthority = options.remoteAuthority;
|
||||
|
||||
const fileInputs = options.fileInputs;
|
||||
if (fileInputs) {
|
||||
configuration.filesToOpenOrCreate = fileInputs.filesToOpenOrCreate;
|
||||
configuration.filesToDiff = fileInputs.filesToDiff;
|
||||
configuration.filesToWait = fileInputs.filesToWait;
|
||||
const filesToOpen = options.filesToOpen;
|
||||
if (filesToOpen) {
|
||||
configuration.filesToOpenOrCreate = filesToOpen.filesToOpenOrCreate;
|
||||
configuration.filesToDiff = filesToOpen.filesToDiff;
|
||||
configuration.filesToWait = filesToOpen.filesToWait;
|
||||
}
|
||||
|
||||
// if we know the backup folder upfront (for empty windows to restore), we can set it
|
||||
@@ -1385,18 +1436,18 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
|
||||
// New window
|
||||
if (!window) {
|
||||
const windowConfig = this.configurationService.getValue<IWindowSettings>('window');
|
||||
const windowConfig = this.configurationService.getValue<IWindowSettings | undefined>('window');
|
||||
const state = this.getNewWindowState(configuration);
|
||||
|
||||
// Window state is not from a previous session: only allow fullscreen if we inherit it or user wants fullscreen
|
||||
let allowFullscreen: boolean;
|
||||
if (state.hasDefaultState) {
|
||||
allowFullscreen = (windowConfig?.newWindowDimensions && ['fullscreen', 'inherit', 'offset'].indexOf(windowConfig.newWindowDimensions) >= 0);
|
||||
allowFullscreen = !!(windowConfig?.newWindowDimensions && ['fullscreen', 'inherit', 'offset'].indexOf(windowConfig.newWindowDimensions) >= 0);
|
||||
}
|
||||
|
||||
// Window state is from a previous session: only allow fullscreen when we got updated or user wants to restore
|
||||
else {
|
||||
allowFullscreen = this.lifecycleMainService.wasRestarted || windowConfig?.restoreFullscreen;
|
||||
allowFullscreen = !!(this.lifecycleMainService.wasRestarted || windowConfig?.restoreFullscreen);
|
||||
|
||||
if (allowFullscreen && isMacintosh && WindowsMainService.WINDOWS.some(win => win.isFullScreen)) {
|
||||
// macOS: Electron does not allow to restore multiple windows in
|
||||
@@ -1581,7 +1632,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
state.y = Math.round(displayToUse.bounds.y + (displayToUse.bounds.height / 2) - (state.height! / 2));
|
||||
|
||||
// Check for newWindowDimensions setting and adjust accordingly
|
||||
const windowConfig = this.configurationService.getValue<IWindowSettings>('window');
|
||||
const windowConfig = this.configurationService.getValue<IWindowSettings | undefined>('window');
|
||||
let ensureNoOverlap = true;
|
||||
if (windowConfig?.newWindowDimensions) {
|
||||
if (windowConfig.newWindowDimensions === 'maximized') {
|
||||
@@ -1659,7 +1710,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
const focusedWindow = this.getFocusedWindow() || this.getLastActiveWindow();
|
||||
|
||||
if (focusedWindow) {
|
||||
focusedWindow.sendWhenReady(channel, ...args);
|
||||
focusedWindow.sendWhenReady(channel, CancellationToken.None, ...args);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1669,14 +1720,23 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
continue; // do not send if we are instructed to ignore it
|
||||
}
|
||||
|
||||
window.sendWhenReady(channel, payload);
|
||||
window.sendWhenReady(channel, CancellationToken.None, payload);
|
||||
}
|
||||
}
|
||||
|
||||
getWindowById(windowId: number): ICodeWindow | undefined {
|
||||
const res = WindowsMainService.WINDOWS.filter(window => window.id === windowId);
|
||||
|
||||
return arrays.firstOrDefault(res);
|
||||
return firstOrDefault(res);
|
||||
}
|
||||
|
||||
getWindowByWebContents(webContents: WebContents): ICodeWindow | undefined {
|
||||
const browserWindow = BrowserWindow.fromWebContents(webContents);
|
||||
if (!browserWindow) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return this.getWindowById(browserWindow.id);
|
||||
}
|
||||
|
||||
getWindows(): ICodeWindow[] {
|
||||
|
||||
@@ -0,0 +1,292 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import * as os from 'os';
|
||||
import * as path from 'vs/base/common/path';
|
||||
|
||||
import { restoreWindowsState, getWindowsStateStoreData } from 'vs/platform/windows/electron-main/windowsStateStorage';
|
||||
import { IWindowState as IWindowUIState, WindowMode } from 'vs/platform/windows/electron-main/windows';
|
||||
import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IWindowsState, IWindowState } from 'vs/platform/windows/electron-main/windowsMainService';
|
||||
|
||||
function getUIState(): IWindowUIState {
|
||||
return {
|
||||
x: 0,
|
||||
y: 10,
|
||||
width: 100,
|
||||
height: 200,
|
||||
mode: 0
|
||||
};
|
||||
}
|
||||
|
||||
function toWorkspace(uri: URI): IWorkspaceIdentifier {
|
||||
return {
|
||||
id: '1234',
|
||||
configPath: uri
|
||||
};
|
||||
}
|
||||
function assertEqualURI(u1: URI | undefined, u2: URI | undefined, message?: string): void {
|
||||
assert.equal(u1 && u1.toString(), u2 && u2.toString(), message);
|
||||
}
|
||||
|
||||
function assertEqualWorkspace(w1: IWorkspaceIdentifier | undefined, w2: IWorkspaceIdentifier | undefined, message?: string): void {
|
||||
if (!w1 || !w2) {
|
||||
assert.equal(w1, w2, message);
|
||||
return;
|
||||
}
|
||||
assert.equal(w1.id, w2.id, message);
|
||||
assertEqualURI(w1.configPath, w2.configPath, message);
|
||||
}
|
||||
|
||||
function assertEqualWindowState(expected: IWindowState | undefined, actual: IWindowState | undefined, message?: string) {
|
||||
if (!expected || !actual) {
|
||||
assert.deepEqual(expected, actual, message);
|
||||
return;
|
||||
}
|
||||
assert.equal(expected.backupPath, actual.backupPath, message);
|
||||
assertEqualURI(expected.folderUri, actual.folderUri, message);
|
||||
assert.equal(expected.remoteAuthority, actual.remoteAuthority, message);
|
||||
assertEqualWorkspace(expected.workspace, actual.workspace, message);
|
||||
assert.deepEqual(expected.uiState, actual.uiState, message);
|
||||
}
|
||||
|
||||
function assertEqualWindowsState(expected: IWindowsState, actual: IWindowsState, message?: string) {
|
||||
assertEqualWindowState(expected.lastPluginDevelopmentHostWindow, actual.lastPluginDevelopmentHostWindow, message);
|
||||
assertEqualWindowState(expected.lastActiveWindow, actual.lastActiveWindow, message);
|
||||
assert.equal(expected.openedWindows.length, actual.openedWindows.length, message);
|
||||
for (let i = 0; i < expected.openedWindows.length; i++) {
|
||||
assertEqualWindowState(expected.openedWindows[i], actual.openedWindows[i], message);
|
||||
}
|
||||
}
|
||||
|
||||
function assertRestoring(state: IWindowsState, message?: string) {
|
||||
const stored = getWindowsStateStoreData(state);
|
||||
const restored = restoreWindowsState(stored);
|
||||
assertEqualWindowsState(state, restored, message);
|
||||
}
|
||||
|
||||
const testBackupPath1 = path.join(os.tmpdir(), 'windowStateTest', 'backupFolder1');
|
||||
const testBackupPath2 = path.join(os.tmpdir(), 'windowStateTest', 'backupFolder2');
|
||||
|
||||
const testWSPath = URI.file(path.join(os.tmpdir(), 'windowStateTest', 'test.code-workspace'));
|
||||
const testFolderURI = URI.file(path.join(os.tmpdir(), 'windowStateTest', 'testFolder'));
|
||||
|
||||
const testRemoteFolderURI = URI.parse('foo://bar/c/d');
|
||||
|
||||
suite('Windows State Storing', () => {
|
||||
test('storing and restoring', () => {
|
||||
let windowState: IWindowsState;
|
||||
windowState = {
|
||||
openedWindows: []
|
||||
};
|
||||
assertRestoring(windowState, 'no windows');
|
||||
windowState = {
|
||||
openedWindows: [{ backupPath: testBackupPath1, uiState: getUIState() }]
|
||||
};
|
||||
assertRestoring(windowState, 'empty workspace');
|
||||
|
||||
windowState = {
|
||||
openedWindows: [{ backupPath: testBackupPath1, uiState: getUIState(), workspace: toWorkspace(testWSPath) }]
|
||||
};
|
||||
assertRestoring(windowState, 'workspace');
|
||||
|
||||
windowState = {
|
||||
openedWindows: [{ backupPath: testBackupPath2, uiState: getUIState(), folderUri: testFolderURI }]
|
||||
};
|
||||
assertRestoring(windowState, 'folder');
|
||||
|
||||
windowState = {
|
||||
openedWindows: [{ backupPath: testBackupPath1, uiState: getUIState(), folderUri: testFolderURI }, { backupPath: testBackupPath1, uiState: getUIState(), folderUri: testRemoteFolderURI, remoteAuthority: 'bar' }]
|
||||
};
|
||||
assertRestoring(windowState, 'multiple windows');
|
||||
|
||||
windowState = {
|
||||
lastActiveWindow: { backupPath: testBackupPath2, uiState: getUIState(), folderUri: testFolderURI },
|
||||
openedWindows: []
|
||||
};
|
||||
assertRestoring(windowState, 'lastActiveWindow');
|
||||
|
||||
windowState = {
|
||||
lastPluginDevelopmentHostWindow: { backupPath: testBackupPath2, uiState: getUIState(), folderUri: testFolderURI },
|
||||
openedWindows: []
|
||||
};
|
||||
assertRestoring(windowState, 'lastPluginDevelopmentHostWindow');
|
||||
});
|
||||
|
||||
test('open 1_31', () => {
|
||||
const v1_31_workspace = `{
|
||||
"openedWindows": [],
|
||||
"lastActiveWindow": {
|
||||
"workspace": {
|
||||
"id": "a41787288b5e9cc1a61ba2dd84cd0d80",
|
||||
"configPath": "/home/user/workspaces/code-and-docs.code-workspace"
|
||||
},
|
||||
"backupPath": "/home/user/.config/Code - Insiders/Backups/a41787288b5e9cc1a61ba2dd84cd0d80",
|
||||
"uiState": {
|
||||
"mode": 0,
|
||||
"x": 0,
|
||||
"y": 27,
|
||||
"width": 2560,
|
||||
"height": 1364
|
||||
}
|
||||
}
|
||||
}`;
|
||||
|
||||
let windowsState = restoreWindowsState(JSON.parse(v1_31_workspace));
|
||||
let expected: IWindowsState = {
|
||||
openedWindows: [],
|
||||
lastActiveWindow: {
|
||||
backupPath: '/home/user/.config/Code - Insiders/Backups/a41787288b5e9cc1a61ba2dd84cd0d80',
|
||||
uiState: { mode: WindowMode.Maximized, x: 0, y: 27, width: 2560, height: 1364 },
|
||||
workspace: { id: 'a41787288b5e9cc1a61ba2dd84cd0d80', configPath: URI.file('/home/user/workspaces/code-and-docs.code-workspace') }
|
||||
}
|
||||
};
|
||||
|
||||
assertEqualWindowsState(expected, windowsState, 'v1_31_workspace');
|
||||
|
||||
const v1_31_folder = `{
|
||||
"openedWindows": [],
|
||||
"lastPluginDevelopmentHostWindow": {
|
||||
"folderUri": {
|
||||
"$mid": 1,
|
||||
"fsPath": "/home/user/workspaces/testing/customdata",
|
||||
"external": "file:///home/user/workspaces/testing/customdata",
|
||||
"path": "/home/user/workspaces/testing/customdata",
|
||||
"scheme": "file"
|
||||
},
|
||||
"uiState": {
|
||||
"mode": 1,
|
||||
"x": 593,
|
||||
"y": 617,
|
||||
"width": 1625,
|
||||
"height": 595
|
||||
}
|
||||
}
|
||||
}`;
|
||||
|
||||
windowsState = restoreWindowsState(JSON.parse(v1_31_folder));
|
||||
expected = {
|
||||
openedWindows: [],
|
||||
lastPluginDevelopmentHostWindow: {
|
||||
uiState: { mode: WindowMode.Normal, x: 593, y: 617, width: 1625, height: 595 },
|
||||
folderUri: URI.parse('file:///home/user/workspaces/testing/customdata')
|
||||
}
|
||||
};
|
||||
assertEqualWindowsState(expected, windowsState, 'v1_31_folder');
|
||||
|
||||
const v1_31_empty_window = ` {
|
||||
"openedWindows": [
|
||||
],
|
||||
"lastActiveWindow": {
|
||||
"backupPath": "C:\\\\Users\\\\Mike\\\\AppData\\\\Roaming\\\\Code\\\\Backups\\\\1549538599815",
|
||||
"uiState": {
|
||||
"mode": 0,
|
||||
"x": -8,
|
||||
"y": -8,
|
||||
"width": 2576,
|
||||
"height": 1344
|
||||
}
|
||||
}
|
||||
}`;
|
||||
|
||||
windowsState = restoreWindowsState(JSON.parse(v1_31_empty_window));
|
||||
expected = {
|
||||
openedWindows: [],
|
||||
lastActiveWindow: {
|
||||
backupPath: 'C:\\Users\\Mike\\AppData\\Roaming\\Code\\Backups\\1549538599815',
|
||||
uiState: { mode: WindowMode.Maximized, x: -8, y: -8, width: 2576, height: 1344 }
|
||||
}
|
||||
};
|
||||
assertEqualWindowsState(expected, windowsState, 'v1_31_empty_window');
|
||||
|
||||
});
|
||||
|
||||
test('open 1_32', () => {
|
||||
const v1_32_workspace = `{
|
||||
"openedWindows": [],
|
||||
"lastActiveWindow": {
|
||||
"workspaceIdentifier": {
|
||||
"id": "53b714b46ef1a2d4346568b4f591028c",
|
||||
"configURIPath": "file:///home/user/workspaces/testing/custom.code-workspace"
|
||||
},
|
||||
"backupPath": "/home/user/.config/code-oss-dev/Backups/53b714b46ef1a2d4346568b4f591028c",
|
||||
"uiState": {
|
||||
"mode": 0,
|
||||
"x": 0,
|
||||
"y": 27,
|
||||
"width": 2560,
|
||||
"height": 1364
|
||||
}
|
||||
}
|
||||
}`;
|
||||
|
||||
let windowsState = restoreWindowsState(JSON.parse(v1_32_workspace));
|
||||
let expected: IWindowsState = {
|
||||
openedWindows: [],
|
||||
lastActiveWindow: {
|
||||
backupPath: '/home/user/.config/code-oss-dev/Backups/53b714b46ef1a2d4346568b4f591028c',
|
||||
uiState: { mode: WindowMode.Maximized, x: 0, y: 27, width: 2560, height: 1364 },
|
||||
workspace: { id: '53b714b46ef1a2d4346568b4f591028c', configPath: URI.parse('file:///home/user/workspaces/testing/custom.code-workspace') }
|
||||
}
|
||||
};
|
||||
|
||||
assertEqualWindowsState(expected, windowsState, 'v1_32_workspace');
|
||||
|
||||
const v1_32_folder = `{
|
||||
"openedWindows": [],
|
||||
"lastActiveWindow": {
|
||||
"folder": "file:///home/user/workspaces/testing/folding",
|
||||
"backupPath": "/home/user/.config/code-oss-dev/Backups/1daac1621c6c06f9e916ac8062e5a1b5",
|
||||
"uiState": {
|
||||
"mode": 1,
|
||||
"x": 625,
|
||||
"y": 263,
|
||||
"width": 1718,
|
||||
"height": 953
|
||||
}
|
||||
}
|
||||
}`;
|
||||
|
||||
windowsState = restoreWindowsState(JSON.parse(v1_32_folder));
|
||||
expected = {
|
||||
openedWindows: [],
|
||||
lastActiveWindow: {
|
||||
backupPath: '/home/user/.config/code-oss-dev/Backups/1daac1621c6c06f9e916ac8062e5a1b5',
|
||||
uiState: { mode: WindowMode.Normal, x: 625, y: 263, width: 1718, height: 953 },
|
||||
folderUri: URI.parse('file:///home/user/workspaces/testing/folding')
|
||||
}
|
||||
};
|
||||
assertEqualWindowsState(expected, windowsState, 'v1_32_folder');
|
||||
|
||||
const v1_32_empty_window = ` {
|
||||
"openedWindows": [
|
||||
],
|
||||
"lastActiveWindow": {
|
||||
"backupPath": "/home/user/.config/code-oss-dev/Backups/1549539668998",
|
||||
"uiState": {
|
||||
"mode": 1,
|
||||
"x": 768,
|
||||
"y": 336,
|
||||
"width": 1024,
|
||||
"height": 768
|
||||
}
|
||||
}
|
||||
}`;
|
||||
|
||||
windowsState = restoreWindowsState(JSON.parse(v1_32_empty_window));
|
||||
expected = {
|
||||
openedWindows: [],
|
||||
lastActiveWindow: {
|
||||
backupPath: '/home/user/.config/code-oss-dev/Backups/1549539668998',
|
||||
uiState: { mode: WindowMode.Normal, x: 768, y: 336, width: 1024, height: 768 }
|
||||
}
|
||||
};
|
||||
assertEqualWindowsState(expected, windowsState, 'v1_32_empty_window');
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
Reference in New Issue
Block a user