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:
Charles Gagnon
2021-06-17 08:17:11 -07:00
committed by GitHub
parent fdcb97c7f7
commit 3cb2f552a6
2582 changed files with 124827 additions and 87099 deletions

View File

@@ -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;

File diff suppressed because it is too large Load Diff

View File

@@ -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 { }

View File

@@ -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 {

View File

@@ -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
}

View File

@@ -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;