mirror of
https://github.com/coder/code-server.git
synced 2026-05-27 06:37:27 +02:00
Merge commit 'be3e8236086165e5e45a5a10783823874b3f3ebd' as 'lib/vscode'
This commit is contained in:
56
lib/vscode/src/vs/platform/windows/common/windowTracker.ts
Normal file
56
lib/vscode/src/vs/platform/windows/common/windowTracker.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async';
|
||||
|
||||
export class ActiveWindowManager extends Disposable {
|
||||
|
||||
private readonly disposables = this._register(new DisposableStore());
|
||||
private firstActiveWindowIdPromise: CancelablePromise<number | undefined> | undefined;
|
||||
|
||||
private activeWindowId: number | undefined;
|
||||
|
||||
constructor({ onDidOpenWindow, onDidFocusWindow, getActiveWindowId }: {
|
||||
onDidOpenWindow: Event<number>,
|
||||
onDidFocusWindow: Event<number>,
|
||||
getActiveWindowId(): Promise<number | undefined>
|
||||
}) {
|
||||
super();
|
||||
|
||||
// remember last active window id upon events
|
||||
const onActiveWindowChange = Event.latch(Event.any(onDidOpenWindow, onDidFocusWindow));
|
||||
onActiveWindowChange(this.setActiveWindow, this, this.disposables);
|
||||
|
||||
// resolve current active window
|
||||
this.firstActiveWindowIdPromise = createCancelablePromise(() => getActiveWindowId());
|
||||
(async () => {
|
||||
try {
|
||||
const windowId = await this.firstActiveWindowIdPromise;
|
||||
this.activeWindowId = (typeof this.activeWindowId === 'number') ? this.activeWindowId : windowId;
|
||||
} catch (error) {
|
||||
// ignore
|
||||
} finally {
|
||||
this.firstActiveWindowIdPromise = undefined;
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
private setActiveWindow(windowId: number | undefined) {
|
||||
if (this.firstActiveWindowIdPromise) {
|
||||
this.firstActiveWindowIdPromise.cancel();
|
||||
this.firstActiveWindowIdPromise = undefined;
|
||||
}
|
||||
|
||||
this.activeWindowId = windowId;
|
||||
}
|
||||
|
||||
async getActiveClientId(): Promise<string | undefined> {
|
||||
const id = this.firstActiveWindowIdPromise ? (await this.firstActiveWindowIdPromise) : this.activeWindowId;
|
||||
|
||||
return `window:${id}`;
|
||||
}
|
||||
}
|
||||
267
lib/vscode/src/vs/platform/windows/common/windows.ts
Normal file
267
lib/vscode/src/vs/platform/windows/common/windows.ts
Normal file
@@ -0,0 +1,267 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
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';
|
||||
import { NativeParsedArgs } from 'vs/platform/environment/common/argv';
|
||||
import { LogLevel } from 'vs/platform/log/common/log';
|
||||
import { ExportData } from 'vs/base/common/performance';
|
||||
|
||||
export const WindowMinimumSize = {
|
||||
WIDTH: 400,
|
||||
WIDTH_WITH_VERTICAL_PANEL: 600,
|
||||
HEIGHT: 270
|
||||
};
|
||||
|
||||
export interface IBaseOpenWindowsOptions {
|
||||
forceReuseWindow?: boolean;
|
||||
}
|
||||
|
||||
export interface IOpenWindowOptions extends IBaseOpenWindowsOptions {
|
||||
forceNewWindow?: boolean;
|
||||
preferNewWindow?: boolean;
|
||||
|
||||
noRecentEntry?: boolean;
|
||||
|
||||
addMode?: boolean;
|
||||
|
||||
diffMode?: boolean;
|
||||
gotoLineMode?: boolean;
|
||||
|
||||
waitMarkerFileURI?: URI;
|
||||
}
|
||||
|
||||
export interface IAddFoldersRequest {
|
||||
foldersToAdd: UriComponents[];
|
||||
}
|
||||
|
||||
export interface IOpenedWindow {
|
||||
id: number;
|
||||
workspace?: IWorkspaceIdentifier;
|
||||
folderUri?: ISingleFolderWorkspaceIdentifier;
|
||||
title: string;
|
||||
filename?: string;
|
||||
dirty: boolean;
|
||||
}
|
||||
|
||||
export interface IOpenEmptyWindowOptions extends IBaseOpenWindowsOptions {
|
||||
remoteAuthority?: string;
|
||||
}
|
||||
|
||||
export type IWindowOpenable = IWorkspaceToOpen | IFolderToOpen | IFileToOpen;
|
||||
|
||||
export interface IBaseWindowOpenable {
|
||||
label?: string;
|
||||
}
|
||||
|
||||
export interface IWorkspaceToOpen extends IBaseWindowOpenable {
|
||||
workspaceUri: URI;
|
||||
}
|
||||
|
||||
export interface IFolderToOpen extends IBaseWindowOpenable {
|
||||
folderUri: URI;
|
||||
}
|
||||
|
||||
export interface IFileToOpen extends IBaseWindowOpenable {
|
||||
fileUri: URI;
|
||||
}
|
||||
|
||||
export function isWorkspaceToOpen(uriToOpen: IWindowOpenable): uriToOpen is IWorkspaceToOpen {
|
||||
return !!(uriToOpen as IWorkspaceToOpen).workspaceUri;
|
||||
}
|
||||
|
||||
export function isFolderToOpen(uriToOpen: IWindowOpenable): uriToOpen is IFolderToOpen {
|
||||
return !!(uriToOpen as IFolderToOpen).folderUri;
|
||||
}
|
||||
|
||||
export function isFileToOpen(uriToOpen: IWindowOpenable): uriToOpen is IFileToOpen {
|
||||
return !!(uriToOpen as IFileToOpen).fileUri;
|
||||
}
|
||||
|
||||
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);
|
||||
const menuBarVisibility = configurationService.getValue<MenuBarVisibility>('window.menuBarVisibility');
|
||||
|
||||
if (titleBarStyle === 'native' && menuBarVisibility === 'compact') {
|
||||
return 'default';
|
||||
} else {
|
||||
return menuBarVisibility;
|
||||
}
|
||||
}
|
||||
|
||||
export interface IWindowsConfiguration {
|
||||
window: IWindowSettings;
|
||||
}
|
||||
|
||||
export interface IWindowSettings {
|
||||
openFilesInNewWindow: 'on' | 'off' | 'default';
|
||||
openFoldersInNewWindow: 'on' | 'off' | 'default';
|
||||
openWithoutArgumentsInNewWindow: 'on' | 'off';
|
||||
restoreWindows: 'all' | 'folders' | 'one' | 'none';
|
||||
restoreFullscreen: boolean;
|
||||
zoomLevel: number;
|
||||
titleBarStyle: 'native' | 'custom';
|
||||
autoDetectHighContrast: boolean;
|
||||
menuBarVisibility: MenuBarVisibility;
|
||||
newWindowDimensions: 'default' | 'inherit' | 'offset' | 'maximized' | 'fullscreen';
|
||||
nativeTabs: boolean;
|
||||
nativeFullScreen: boolean;
|
||||
enableMenuBarMnemonics: boolean;
|
||||
closeWhenEmpty: boolean;
|
||||
clickThroughInactive: boolean;
|
||||
enableExperimentalProxyLoginDialog: boolean;
|
||||
}
|
||||
|
||||
export function getTitleBarStyle(configurationService: IConfigurationService, environment: IEnvironmentService, isExtensionDevelopment = environment.isExtensionDevelopment): '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
|
||||
}
|
||||
|
||||
if (configuration) {
|
||||
const useNativeTabs = isMacintosh && configuration.nativeTabs === true;
|
||||
if (useNativeTabs) {
|
||||
return 'native'; // native tabs on sierra do not work with custom title style
|
||||
}
|
||||
|
||||
const useSimpleFullScreen = isMacintosh && configuration.nativeFullScreen === false;
|
||||
if (useSimpleFullScreen) {
|
||||
return 'native'; // simple fullscreen does not work well with custom title style (https://github.com/microsoft/vscode/issues/63291)
|
||||
}
|
||||
|
||||
const style = configuration.titleBarStyle;
|
||||
if (style === 'native' || style === 'custom') {
|
||||
return style;
|
||||
}
|
||||
}
|
||||
|
||||
return isLinux ? 'native' : 'custom'; // default to custom on all macOS and Windows
|
||||
}
|
||||
|
||||
export interface IPath extends IPathData {
|
||||
|
||||
// the file path to open within the instance
|
||||
fileUri?: URI;
|
||||
}
|
||||
|
||||
export interface IPathData {
|
||||
|
||||
// the file path to open within the instance
|
||||
fileUri?: UriComponents;
|
||||
|
||||
// the line number in the file path to open
|
||||
lineNumber?: number;
|
||||
|
||||
// the column number in the file path to open
|
||||
columnNumber?: number;
|
||||
|
||||
// a hint that the file exists. if true, the
|
||||
// file exists, if false it does not. with
|
||||
// undefined the state is unknown.
|
||||
exists?: boolean;
|
||||
|
||||
// Specifies if the file should be only be opened if it exists
|
||||
openOnlyIfExists?: boolean;
|
||||
|
||||
// Specifies an optional id to override the editor used to edit the resource, e.g. custom editor.
|
||||
overrideId?: string;
|
||||
}
|
||||
|
||||
export interface IPathsToWaitFor extends IPathsToWaitForData {
|
||||
paths: IPath[];
|
||||
waitMarkerFileUri: URI;
|
||||
}
|
||||
|
||||
interface IPathsToWaitForData {
|
||||
paths: IPathData[];
|
||||
waitMarkerFileUri: UriComponents;
|
||||
}
|
||||
|
||||
export interface IOpenFileRequest {
|
||||
filesToOpenOrCreate?: IPathData[];
|
||||
filesToDiff?: IPathData[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Additional context for the request on native only.
|
||||
*/
|
||||
export interface INativeOpenFileRequest extends IOpenFileRequest {
|
||||
termProgram?: string;
|
||||
filesToWait?: IPathsToWaitForData;
|
||||
}
|
||||
|
||||
export interface INativeRunActionInWindowRequest {
|
||||
id: string;
|
||||
from: 'menu' | 'touchbar' | 'mouse';
|
||||
args?: any[];
|
||||
}
|
||||
|
||||
export interface INativeRunKeybindingInWindowRequest {
|
||||
userSettingsLabel: string;
|
||||
}
|
||||
|
||||
export interface IColorScheme {
|
||||
dark: boolean;
|
||||
highContrast: boolean;
|
||||
}
|
||||
|
||||
export interface IWindowConfiguration {
|
||||
sessionId: string;
|
||||
|
||||
remoteAuthority?: string;
|
||||
|
||||
colorScheme: IColorScheme;
|
||||
autoDetectHighContrast?: boolean;
|
||||
|
||||
filesToOpenOrCreate?: IPath[];
|
||||
filesToDiff?: IPath[];
|
||||
}
|
||||
|
||||
export interface INativeWindowConfiguration extends IWindowConfiguration, NativeParsedArgs {
|
||||
mainPid: number;
|
||||
|
||||
windowId: number;
|
||||
machineId: string;
|
||||
|
||||
appRoot: string;
|
||||
execPath: string;
|
||||
backupPath?: string;
|
||||
|
||||
nodeCachedDataDir?: string;
|
||||
partsSplashPath: string;
|
||||
|
||||
workspace?: IWorkspaceIdentifier;
|
||||
folderUri?: ISingleFolderWorkspaceIdentifier;
|
||||
|
||||
isInitialStartup?: boolean;
|
||||
logLevel: LogLevel;
|
||||
zoomLevel?: number;
|
||||
fullscreen?: boolean;
|
||||
maximized?: boolean;
|
||||
accessibilitySupport?: boolean;
|
||||
perfEntries: ExportData;
|
||||
|
||||
userEnv: IProcessEnvironment;
|
||||
filesToWait?: IPathsToWaitFor;
|
||||
}
|
||||
|
||||
/**
|
||||
* According to Electron docs: `scale := 1.2 ^ level`.
|
||||
* https://github.com/electron/electron/blob/master/docs/api/web-contents.md#contentssetzoomlevellevel
|
||||
*/
|
||||
export function zoomLevelToZoomFactor(zoomLevel = 0): number {
|
||||
return Math.pow(1.2, zoomLevel);
|
||||
}
|
||||
142
lib/vscode/src/vs/platform/windows/electron-main/windows.ts
Normal file
142
lib/vscode/src/vs/platform/windows/electron-main/windows.ts
Normal file
@@ -0,0 +1,142 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IWindowOpenable, IOpenEmptyWindowOptions, INativeWindowConfiguration } from 'vs/platform/windows/common/windows';
|
||||
import { OpenContext } from 'vs/platform/windows/node/window';
|
||||
import { NativeParsedArgs } from 'vs/platform/environment/common/argv';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
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 { IDisposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
export interface IWindowState {
|
||||
width?: number;
|
||||
height?: number;
|
||||
x?: number;
|
||||
y?: number;
|
||||
mode?: WindowMode;
|
||||
display?: number;
|
||||
}
|
||||
|
||||
export const enum WindowMode {
|
||||
Maximized,
|
||||
Normal,
|
||||
Minimized, // not used anymore, but also cannot remove due to existing stored UI state (needs migration)
|
||||
Fullscreen
|
||||
}
|
||||
|
||||
export interface ICodeWindow extends IDisposable {
|
||||
|
||||
readonly whenClosedOrLoaded: Promise<void>;
|
||||
|
||||
readonly id: number;
|
||||
readonly win: BrowserWindow;
|
||||
readonly config: INativeWindowConfiguration | undefined;
|
||||
|
||||
readonly openedFolderUri?: URI;
|
||||
readonly openedWorkspace?: IWorkspaceIdentifier;
|
||||
readonly backupPath?: string;
|
||||
|
||||
readonly remoteAuthority?: string;
|
||||
|
||||
readonly isExtensionDevelopmentHost: boolean;
|
||||
readonly isExtensionTestHost: boolean;
|
||||
|
||||
readonly lastFocusTime: number;
|
||||
|
||||
readonly isReady: boolean;
|
||||
ready(): Promise<ICodeWindow>;
|
||||
setReady(): void;
|
||||
|
||||
readonly hasHiddenTitleBarStyle: boolean;
|
||||
|
||||
addTabbedWindow(window: ICodeWindow): void;
|
||||
|
||||
load(config: INativeWindowConfiguration, isReload?: boolean): void;
|
||||
reload(configuration?: INativeWindowConfiguration, cli?: NativeParsedArgs): void;
|
||||
|
||||
focus(options?: { force: boolean }): void;
|
||||
close(): void;
|
||||
|
||||
getBounds(): Rectangle;
|
||||
|
||||
send(channel: string, ...args: any[]): void;
|
||||
sendWhenReady(channel: string, ...args: any[]): void;
|
||||
|
||||
readonly isFullScreen: boolean;
|
||||
toggleFullScreen(): void;
|
||||
|
||||
isMinimized(): boolean;
|
||||
|
||||
setRepresentedFilename(name: string): void;
|
||||
getRepresentedFilename(): string | undefined;
|
||||
|
||||
setDocumentEdited(edited: boolean): void;
|
||||
isDocumentEdited(): boolean;
|
||||
|
||||
handleTitleDoubleClick(): void;
|
||||
|
||||
updateTouchBar(items: ISerializableCommandAction[][]): void;
|
||||
|
||||
serializeWindowState(): IWindowState;
|
||||
}
|
||||
|
||||
export const IWindowsMainService = createDecorator<IWindowsMainService>('windowsMainService');
|
||||
|
||||
export interface IWindowsCountChangedEvent {
|
||||
readonly oldCount: number;
|
||||
readonly newCount: number;
|
||||
}
|
||||
|
||||
export interface IWindowsMainService {
|
||||
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
readonly onWindowOpened: Event<ICodeWindow>;
|
||||
readonly onWindowReady: Event<ICodeWindow>;
|
||||
readonly onWindowsCountChanged: Event<IWindowsCountChangedEvent>;
|
||||
|
||||
open(openConfig: IOpenConfiguration): ICodeWindow[];
|
||||
openEmptyWindow(openConfig: IOpenEmptyConfiguration, options?: IOpenEmptyWindowOptions): ICodeWindow[];
|
||||
openExtensionDevelopmentHostWindow(extensionDevelopmentPath: string[], openConfig: IOpenConfiguration): ICodeWindow[];
|
||||
|
||||
sendToFocused(channel: string, ...args: any[]): void;
|
||||
sendToAll(channel: string, payload?: any, windowIdsToIgnore?: number[]): void;
|
||||
|
||||
getFocusedWindow(): ICodeWindow | undefined;
|
||||
getLastActiveWindow(): ICodeWindow | undefined;
|
||||
|
||||
getWindowById(windowId: number): ICodeWindow | undefined;
|
||||
getWindows(): ICodeWindow[];
|
||||
getWindowCount(): number;
|
||||
}
|
||||
|
||||
export interface IBaseOpenConfiguration {
|
||||
readonly context: OpenContext;
|
||||
readonly contextWindowId?: number;
|
||||
}
|
||||
|
||||
export interface IOpenConfiguration extends IBaseOpenConfiguration {
|
||||
readonly cli: NativeParsedArgs;
|
||||
readonly userEnv?: IProcessEnvironment;
|
||||
readonly urisToOpen?: IWindowOpenable[];
|
||||
readonly waitMarkerFileURI?: URI;
|
||||
readonly preferNewWindow?: boolean;
|
||||
readonly forceNewWindow?: boolean;
|
||||
readonly forceNewTabbedWindow?: boolean;
|
||||
readonly forceReuseWindow?: boolean;
|
||||
readonly forceEmpty?: boolean;
|
||||
readonly diffMode?: boolean;
|
||||
addMode?: boolean;
|
||||
readonly gotoLineMode?: boolean;
|
||||
readonly initialStartup?: boolean;
|
||||
readonly noRecentEntry?: boolean;
|
||||
}
|
||||
|
||||
export interface IOpenEmptyConfiguration extends IBaseOpenConfiguration { }
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,93 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { IWindowState as IWindowUIState } from 'vs/platform/windows/electron-main/windows';
|
||||
import { IWindowState, IWindowsState } from 'vs/platform/windows/electron-main/windowsMainService';
|
||||
|
||||
export type WindowsStateStorageData = object;
|
||||
|
||||
interface ISerializedWindowsState {
|
||||
lastActiveWindow?: ISerializedWindowState;
|
||||
lastPluginDevelopmentHostWindow?: ISerializedWindowState;
|
||||
openedWindows: ISerializedWindowState[];
|
||||
}
|
||||
|
||||
interface ISerializedWindowState {
|
||||
workspaceIdentifier?: { id: string; configURIPath: string };
|
||||
folder?: string;
|
||||
backupPath?: string;
|
||||
remoteAuthority?: string;
|
||||
uiState: IWindowUIState;
|
||||
|
||||
// deprecated
|
||||
folderUri?: UriComponents;
|
||||
folderPath?: string;
|
||||
workspace?: { id: string; configPath: string };
|
||||
}
|
||||
|
||||
export function restoreWindowsState(data: WindowsStateStorageData | undefined): IWindowsState {
|
||||
const result: IWindowsState = { openedWindows: [] };
|
||||
const windowsState = data as ISerializedWindowsState || { openedWindows: [] };
|
||||
|
||||
if (windowsState.lastActiveWindow) {
|
||||
result.lastActiveWindow = restoreWindowState(windowsState.lastActiveWindow);
|
||||
}
|
||||
|
||||
if (windowsState.lastPluginDevelopmentHostWindow) {
|
||||
result.lastPluginDevelopmentHostWindow = restoreWindowState(windowsState.lastPluginDevelopmentHostWindow);
|
||||
}
|
||||
|
||||
if (Array.isArray(windowsState.openedWindows)) {
|
||||
result.openedWindows = windowsState.openedWindows.map(windowState => restoreWindowState(windowState));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function restoreWindowState(windowState: ISerializedWindowState): IWindowState {
|
||||
const result: IWindowState = { uiState: windowState.uiState };
|
||||
if (windowState.backupPath) {
|
||||
result.backupPath = windowState.backupPath;
|
||||
}
|
||||
|
||||
if (windowState.remoteAuthority) {
|
||||
result.remoteAuthority = windowState.remoteAuthority;
|
||||
}
|
||||
|
||||
if (windowState.folder) {
|
||||
result.folderUri = URI.parse(windowState.folder);
|
||||
} else if (windowState.folderUri) {
|
||||
result.folderUri = URI.revive(windowState.folderUri);
|
||||
} else if (windowState.folderPath) {
|
||||
result.folderUri = URI.file(windowState.folderPath);
|
||||
}
|
||||
|
||||
if (windowState.workspaceIdentifier) {
|
||||
result.workspace = { id: windowState.workspaceIdentifier.id, configPath: URI.parse(windowState.workspaceIdentifier.configURIPath) };
|
||||
} else if (windowState.workspace) {
|
||||
result.workspace = { id: windowState.workspace.id, configPath: URI.file(windowState.workspace.configPath) };
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function getWindowsStateStoreData(windowsState: IWindowsState): WindowsStateStorageData {
|
||||
return {
|
||||
lastActiveWindow: windowsState.lastActiveWindow && serializeWindowState(windowsState.lastActiveWindow),
|
||||
lastPluginDevelopmentHostWindow: windowsState.lastPluginDevelopmentHostWindow && serializeWindowState(windowsState.lastPluginDevelopmentHostWindow),
|
||||
openedWindows: windowsState.openedWindows.map(ws => serializeWindowState(ws))
|
||||
};
|
||||
}
|
||||
|
||||
function serializeWindowState(windowState: IWindowState): ISerializedWindowState {
|
||||
return {
|
||||
workspaceIdentifier: windowState.workspace && { id: windowState.workspace.id, configURIPath: windowState.workspace.configPath.toString() },
|
||||
folder: windowState.folderUri && windowState.folderUri.toString(),
|
||||
backupPath: windowState.backupPath,
|
||||
remoteAuthority: windowState.remoteAuthority,
|
||||
uiState: windowState.uiState
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { webFrame } from 'vs/base/parts/sandbox/electron-sandbox/globals';
|
||||
import { zoomLevelToZoomFactor } from 'vs/platform/windows/common/windows';
|
||||
import { setZoomFactor, setZoomLevel, getZoomLevel } from 'vs/base/browser/browser';
|
||||
|
||||
/**
|
||||
* Apply a zoom level to the window. Also sets it in our in-memory
|
||||
* browser helper so that it can be accessed in non-electron layers.
|
||||
*/
|
||||
export function applyZoom(zoomLevel: number): void {
|
||||
webFrame.setZoomLevel(zoomLevel);
|
||||
setZoomFactor(zoomLevelToZoomFactor(zoomLevel));
|
||||
// Cannot be trusted because the webFrame might take some time
|
||||
// until it really applies the new zoom level
|
||||
// See https://github.com/microsoft/vscode/issues/26151
|
||||
setZoomLevel(zoomLevel, false /* isTrusted */);
|
||||
}
|
||||
|
||||
export function zoomIn(): void {
|
||||
applyZoom(getZoomLevel() + 1);
|
||||
}
|
||||
|
||||
export function zoomOut(): void {
|
||||
applyZoom(getZoomLevel() - 1);
|
||||
}
|
||||
150
lib/vscode/src/vs/platform/windows/node/window.ts
Normal file
150
lib/vscode/src/vs/platform/windows/node/window.ts
Normal file
@@ -0,0 +1,150 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import * as extpath from 'vs/base/common/extpath';
|
||||
import { IWorkspaceIdentifier, IResolvedWorkspace, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { extUriBiasedIgnorePathCase } from 'vs/base/common/resources';
|
||||
|
||||
export const enum OpenContext {
|
||||
|
||||
// opening when running from the command line
|
||||
CLI,
|
||||
|
||||
// macOS only: opening from the dock (also when opening files to a running instance from desktop)
|
||||
DOCK,
|
||||
|
||||
// opening from the main application window
|
||||
MENU,
|
||||
|
||||
// opening from a file or folder dialog
|
||||
DIALOG,
|
||||
|
||||
// opening from the OS's UI
|
||||
DESKTOP,
|
||||
|
||||
// opening through the API
|
||||
API
|
||||
}
|
||||
|
||||
export interface IWindowContext {
|
||||
openedWorkspace?: IWorkspaceIdentifier;
|
||||
openedFolderUri?: URI;
|
||||
|
||||
extensionDevelopmentPath?: string[];
|
||||
lastFocusTime: number;
|
||||
}
|
||||
|
||||
export interface IBestWindowOrFolderOptions<W extends IWindowContext> {
|
||||
windows: W[];
|
||||
newWindow: boolean;
|
||||
context: OpenContext;
|
||||
fileUri?: URI;
|
||||
codeSettingsFolder?: string;
|
||||
localWorkspaceResolver: (workspace: IWorkspaceIdentifier) => IResolvedWorkspace | null;
|
||||
}
|
||||
|
||||
export function findBestWindowOrFolderForFile<W extends IWindowContext>({ windows, newWindow, context, fileUri, localWorkspaceResolver: workspaceResolver }: IBestWindowOrFolderOptions<W>): W | undefined {
|
||||
if (!newWindow && fileUri && (context === OpenContext.DESKTOP || context === OpenContext.CLI || context === OpenContext.DOCK)) {
|
||||
const windowOnFilePath = findWindowOnFilePath(windows, fileUri, workspaceResolver);
|
||||
if (windowOnFilePath) {
|
||||
return windowOnFilePath;
|
||||
}
|
||||
}
|
||||
return !newWindow ? getLastActiveWindow(windows) : undefined;
|
||||
}
|
||||
|
||||
function findWindowOnFilePath<W extends IWindowContext>(windows: W[], fileUri: URI, localWorkspaceResolver: (workspace: IWorkspaceIdentifier) => IResolvedWorkspace | null): W | null {
|
||||
|
||||
// First check for windows with workspaces that have a parent folder of the provided path opened
|
||||
for (const window of windows) {
|
||||
const workspace = window.openedWorkspace;
|
||||
if (workspace) {
|
||||
const resolvedWorkspace = localWorkspaceResolver(workspace);
|
||||
if (resolvedWorkspace) {
|
||||
// workspace could be resolved: It's in the local file system
|
||||
if (resolvedWorkspace.folders.some(folder => extUriBiasedIgnorePathCase.isEqualOrParent(fileUri, folder.uri))) {
|
||||
return window;
|
||||
}
|
||||
} else {
|
||||
// use the config path instead
|
||||
if (extUriBiasedIgnorePathCase.isEqualOrParent(fileUri, workspace.configPath)) {
|
||||
return window;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Then go with single folder windows that are parent of the provided file path
|
||||
const singleFolderWindowsOnFilePath = windows.filter(window => window.openedFolderUri && extUriBiasedIgnorePathCase.isEqualOrParent(fileUri, window.openedFolderUri));
|
||||
if (singleFolderWindowsOnFilePath.length) {
|
||||
return singleFolderWindowsOnFilePath.sort((a, b) => -(a.openedFolderUri!.path.length - b.openedFolderUri!.path.length))[0];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function getLastActiveWindow<W extends IWindowContext>(windows: W[]): W | undefined {
|
||||
const lastFocusedDate = Math.max.apply(Math, windows.map(window => window.lastFocusTime));
|
||||
|
||||
return windows.find(window => window.lastFocusTime === lastFocusedDate);
|
||||
}
|
||||
|
||||
export function findWindowOnWorkspace<W extends IWindowContext>(windows: W[], workspace: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier)): W | null {
|
||||
if (isSingleFolderWorkspaceIdentifier(workspace)) {
|
||||
for (const window of windows) {
|
||||
// match on folder
|
||||
if (isSingleFolderWorkspaceIdentifier(workspace)) {
|
||||
if (window.openedFolderUri && extUriBiasedIgnorePathCase.isEqual(window.openedFolderUri, workspace)) {
|
||||
return window;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (isWorkspaceIdentifier(workspace)) {
|
||||
for (const window of windows) {
|
||||
// match on workspace
|
||||
if (window.openedWorkspace && window.openedWorkspace.id === workspace.id) {
|
||||
return window;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function findWindowOnExtensionDevelopmentPath<W extends IWindowContext>(windows: W[], extensionDevelopmentPaths: string[]): W | null {
|
||||
|
||||
const matches = (uriString: string): boolean => {
|
||||
return extensionDevelopmentPaths.some(p => extpath.isEqual(p, uriString, !platform.isLinux /* ignorecase */));
|
||||
};
|
||||
|
||||
for (const window of windows) {
|
||||
// match on extension development path. The path can be one or more paths or uri strings, using paths.isEqual is not 100% correct but good enough
|
||||
const currPaths = window.extensionDevelopmentPath;
|
||||
if (currPaths?.some(p => matches(p))) {
|
||||
return window;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function findWindowOnWorkspaceOrFolderUri<W extends IWindowContext>(windows: W[], uri: URI | undefined): W | null {
|
||||
if (!uri) {
|
||||
return null;
|
||||
}
|
||||
for (const window of windows) {
|
||||
// check for workspace config path
|
||||
if (window.openedWorkspace && extUriBiasedIgnorePathCase.isEqual(window.openedWorkspace.configPath, uri)) {
|
||||
return window;
|
||||
}
|
||||
|
||||
// check for folder path
|
||||
if (window.openedFolderUri && extUriBiasedIgnorePathCase.isEqual(window.openedFolderUri, uri)) {
|
||||
return window;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
127
lib/vscode/src/vs/platform/windows/test/node/window.test.ts
Normal file
127
lib/vscode/src/vs/platform/windows/test/node/window.test.ts
Normal file
@@ -0,0 +1,127 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 path from 'vs/base/common/path';
|
||||
import { IBestWindowOrFolderOptions, IWindowContext, findBestWindowOrFolderForFile, OpenContext } from 'vs/platform/windows/node/window';
|
||||
import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { toWorkspaceFolders } from 'vs/platform/workspace/common/workspace';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { getPathFromAmdModule } from 'vs/base/common/amd';
|
||||
|
||||
const fixturesFolder = getPathFromAmdModule(require, './fixtures');
|
||||
|
||||
const testWorkspace: IWorkspaceIdentifier = {
|
||||
id: Date.now().toString(),
|
||||
configPath: URI.file(path.join(fixturesFolder, 'workspaces.json'))
|
||||
};
|
||||
|
||||
const testWorkspaceFolders = toWorkspaceFolders([{ path: path.join(fixturesFolder, 'vscode_workspace_1_folder') }, { path: path.join(fixturesFolder, 'vscode_workspace_2_folder') }], testWorkspace.configPath);
|
||||
|
||||
function options(custom?: Partial<IBestWindowOrFolderOptions<IWindowContext>>): IBestWindowOrFolderOptions<IWindowContext> {
|
||||
return {
|
||||
windows: [],
|
||||
newWindow: false,
|
||||
context: OpenContext.CLI,
|
||||
codeSettingsFolder: '_vscode',
|
||||
localWorkspaceResolver: workspace => { return workspace === testWorkspace ? { id: testWorkspace.id, configPath: workspace.configPath, folders: testWorkspaceFolders } : null; },
|
||||
...custom
|
||||
};
|
||||
}
|
||||
|
||||
const vscodeFolderWindow: IWindowContext = { lastFocusTime: 1, openedFolderUri: URI.file(path.join(fixturesFolder, 'vscode_folder')) };
|
||||
const lastActiveWindow: IWindowContext = { lastFocusTime: 3, openedFolderUri: undefined };
|
||||
const noVscodeFolderWindow: IWindowContext = { lastFocusTime: 2, openedFolderUri: URI.file(path.join(fixturesFolder, 'no_vscode_folder')) };
|
||||
const windows: IWindowContext[] = [
|
||||
vscodeFolderWindow,
|
||||
lastActiveWindow,
|
||||
noVscodeFolderWindow,
|
||||
];
|
||||
|
||||
suite('WindowsFinder', () => {
|
||||
|
||||
test('New window without folder when no windows exist', () => {
|
||||
assert.equal(findBestWindowOrFolderForFile(options()), null);
|
||||
assert.equal(findBestWindowOrFolderForFile(options({
|
||||
fileUri: URI.file(path.join(fixturesFolder, 'no_vscode_folder', 'file.txt'))
|
||||
})), null);
|
||||
assert.equal(findBestWindowOrFolderForFile(options({
|
||||
fileUri: URI.file(path.join(fixturesFolder, 'vscode_folder', 'file.txt')),
|
||||
newWindow: true
|
||||
})), null);
|
||||
assert.equal(findBestWindowOrFolderForFile(options({
|
||||
fileUri: URI.file(path.join(fixturesFolder, 'vscode_folder', 'file.txt')),
|
||||
})), null);
|
||||
assert.equal(findBestWindowOrFolderForFile(options({
|
||||
fileUri: URI.file(path.join(fixturesFolder, 'vscode_folder', 'file.txt')),
|
||||
context: OpenContext.API
|
||||
})), null);
|
||||
assert.equal(findBestWindowOrFolderForFile(options({
|
||||
fileUri: URI.file(path.join(fixturesFolder, 'vscode_folder', 'file.txt'))
|
||||
})), null);
|
||||
assert.equal(findBestWindowOrFolderForFile(options({
|
||||
fileUri: URI.file(path.join(fixturesFolder, 'vscode_folder', 'new_folder', 'new_file.txt'))
|
||||
})), null);
|
||||
});
|
||||
|
||||
test('New window without folder when windows exist', () => {
|
||||
assert.equal(findBestWindowOrFolderForFile(options({
|
||||
windows,
|
||||
fileUri: URI.file(path.join(fixturesFolder, 'no_vscode_folder', 'file.txt')),
|
||||
newWindow: true
|
||||
})), null);
|
||||
});
|
||||
|
||||
test('Last active window', () => {
|
||||
assert.equal(findBestWindowOrFolderForFile(options({
|
||||
windows
|
||||
})), lastActiveWindow);
|
||||
assert.equal(findBestWindowOrFolderForFile(options({
|
||||
windows,
|
||||
fileUri: URI.file(path.join(fixturesFolder, 'no_vscode_folder2', 'file.txt'))
|
||||
})), lastActiveWindow);
|
||||
assert.equal(findBestWindowOrFolderForFile(options({
|
||||
windows: [lastActiveWindow, noVscodeFolderWindow],
|
||||
fileUri: URI.file(path.join(fixturesFolder, 'vscode_folder', 'file.txt')),
|
||||
})), lastActiveWindow);
|
||||
assert.equal(findBestWindowOrFolderForFile(options({
|
||||
windows,
|
||||
fileUri: URI.file(path.join(fixturesFolder, 'no_vscode_folder', 'file.txt')),
|
||||
context: OpenContext.API
|
||||
})), lastActiveWindow);
|
||||
});
|
||||
|
||||
test('Existing window with folder', () => {
|
||||
assert.equal(findBestWindowOrFolderForFile(options({
|
||||
windows,
|
||||
fileUri: URI.file(path.join(fixturesFolder, 'no_vscode_folder', 'file.txt'))
|
||||
})), noVscodeFolderWindow);
|
||||
assert.equal(findBestWindowOrFolderForFile(options({
|
||||
windows,
|
||||
fileUri: URI.file(path.join(fixturesFolder, 'vscode_folder', 'file.txt'))
|
||||
})), vscodeFolderWindow);
|
||||
const window: IWindowContext = { lastFocusTime: 1, openedFolderUri: URI.file(path.join(fixturesFolder, 'vscode_folder', 'nested_folder')) };
|
||||
assert.equal(findBestWindowOrFolderForFile(options({
|
||||
windows: [window],
|
||||
fileUri: URI.file(path.join(fixturesFolder, 'vscode_folder', 'nested_folder', 'subfolder', 'file.txt'))
|
||||
})), window);
|
||||
});
|
||||
|
||||
test('More specific existing window wins', () => {
|
||||
const window: IWindowContext = { lastFocusTime: 2, openedFolderUri: URI.file(path.join(fixturesFolder, 'no_vscode_folder')) };
|
||||
const nestedFolderWindow: IWindowContext = { lastFocusTime: 1, openedFolderUri: URI.file(path.join(fixturesFolder, 'no_vscode_folder', 'nested_folder')) };
|
||||
assert.equal(findBestWindowOrFolderForFile(options({
|
||||
windows: [window, nestedFolderWindow],
|
||||
fileUri: URI.file(path.join(fixturesFolder, 'no_vscode_folder', 'nested_folder', 'subfolder', 'file.txt'))
|
||||
})), nestedFolderWindow);
|
||||
});
|
||||
|
||||
test('Workspace folder wins', () => {
|
||||
const window: IWindowContext = { lastFocusTime: 1, openedWorkspace: testWorkspace };
|
||||
assert.equal(findBestWindowOrFolderForFile(options({
|
||||
windows: [window],
|
||||
fileUri: URI.file(path.join(fixturesFolder, 'vscode_workspace_2_folder', 'nested_vscode_folder', 'subfolder', 'file.txt'))
|
||||
})), window);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user