Merge from vscode 313ede61cbad8f9dc748907b3384e059ddddb79a (#7436)

* Merge from vscode 313ede61cbad8f9dc748907b3384e059ddddb79a

* fix strict null checks
This commit is contained in:
Anthony Dresser
2019-09-30 23:35:45 -07:00
committed by GitHub
parent 6ab03053a0
commit 084524cd2d
196 changed files with 2927 additions and 2547 deletions
+10 -8
View File
@@ -21,7 +21,6 @@ process.on('SIGPIPE', () => {
//#endregion
//#region Add support for redirecting the loading of node modules
exports.injectNodeModuleLookupPath = function (injectPath) {
if (!injectPath) {
throw new Error('Missing injectPath');
@@ -37,8 +36,10 @@ exports.injectNodeModuleLookupPath = function (injectPath) {
const originalResolveLookupPaths = Module._resolveLookupPaths;
// @ts-ignore
Module._resolveLookupPaths = function (moduleName, parent) {
const paths = originalResolveLookupPaths(moduleName, parent);
Module._resolveLookupPaths = function (moduleName, parent, newReturn) {
const result = originalResolveLookupPaths(moduleName, parent, newReturn);
const paths = newReturn ? result : result[1];
for (let i = 0, len = paths.length; i < len; i++) {
if (paths[i] === nodeModulesPath) {
paths.splice(i, 0, injectPath);
@@ -46,7 +47,7 @@ exports.injectNodeModuleLookupPath = function (injectPath) {
}
}
return paths;
return result;
};
};
//#endregion
@@ -70,10 +71,11 @@ exports.enableASARSupport = function (nodeModulesPath) {
// @ts-ignore
const originalResolveLookupPaths = Module._resolveLookupPaths;
// @ts-ignore
Module._resolveLookupPaths = function (request, parent) {
const paths = originalResolveLookupPaths(request, parent);
Module._resolveLookupPaths = function (request, parent, newReturn) {
const result = originalResolveLookupPaths(request, parent, newReturn);
const paths = newReturn ? result : result[1];
for (let i = 0, len = paths.length; i < len; i++) {
if (paths[i] === NODE_MODULES_PATH) {
paths.splice(i, 0, NODE_MODULES_ASAR_PATH);
@@ -81,7 +83,7 @@ exports.enableASARSupport = function (nodeModulesPath) {
}
}
return paths;
return result;
};
};
//#endregion
+1 -6
View File
@@ -17,7 +17,7 @@ const paths = require('./paths');
// @ts-ignore
const product = require('../product.json');
// @ts-ignore
const { app, protocol } = require('electron');
const app = require('electron').app;
// Enable portable support
const portable = bootstrap.configurePortable();
@@ -33,11 +33,6 @@ app.setPath('userData', userDataPath);
// Update cwd based on environment and platform
setCurrentWorkingDirectory();
// Register custom schemes with privileges
protocol.registerSchemesAsPrivileged([
{ scheme: 'vscode-resource', privileges: { secure: true, supportFetchAPI: true, corsEnabled: true } }
]);
// Global app listeners
registerListeners();
@@ -0,0 +1,12 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { AbstractEnablePreviewFeatures } from 'sql/workbench/common/enablePreviewFeatures';
export class BrowserEnablePreviewFeatures extends AbstractEnablePreviewFeatures {
protected async getWindowCount(): Promise<number> {
return 1;
}
}
@@ -11,7 +11,7 @@ import { onUnexpectedError } from 'vs/base/common/errors';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IHostService } from 'vs/workbench/services/host/browser/host';
export class EnablePreviewFeatures implements IWorkbenchContribution {
export abstract class AbstractEnablePreviewFeatures implements IWorkbenchContribution {
private static ENABLE_PREVIEW_FEATURES_SHOWN = 'workbench.enablePreviewFeaturesShown';
@@ -22,12 +22,12 @@ export class EnablePreviewFeatures implements IWorkbenchContribution {
@IConfigurationService configurationService: IConfigurationService
) {
let previewFeaturesEnabled = configurationService.getValue('workbench')['enablePreviewFeatures'];
if (previewFeaturesEnabled || storageService.get(EnablePreviewFeatures.ENABLE_PREVIEW_FEATURES_SHOWN, StorageScope.GLOBAL)) {
if (previewFeaturesEnabled || storageService.get(AbstractEnablePreviewFeatures.ENABLE_PREVIEW_FEATURES_SHOWN, StorageScope.GLOBAL)) {
return;
}
Promise.all([
hostService.hasFocus,
hostService.windowCount
this.getWindowCount()
]).then(([focused, count]) => {
if (!focused && count > 1) {
return null;
@@ -42,7 +42,7 @@ export class EnablePreviewFeatures implements IWorkbenchContribution {
label: localize('enablePreviewFeatures.yes', "Yes"),
run: () => {
configurationService.updateValue('workbench.enablePreviewFeatures', true);
storageService.store(EnablePreviewFeatures.ENABLE_PREVIEW_FEATURES_SHOWN, true, StorageScope.GLOBAL);
storageService.store(AbstractEnablePreviewFeatures.ENABLE_PREVIEW_FEATURES_SHOWN, true, StorageScope.GLOBAL);
}
}, {
label: localize('enablePreviewFeatures.no', "No"),
@@ -53,7 +53,7 @@ export class EnablePreviewFeatures implements IWorkbenchContribution {
label: localize('enablePreviewFeatures.never', "No, don't show again"),
run: () => {
configurationService.updateValue('workbench.enablePreviewFeatures', false);
storageService.store(EnablePreviewFeatures.ENABLE_PREVIEW_FEATURES_SHOWN, true, StorageScope.GLOBAL);
storageService.store(AbstractEnablePreviewFeatures.ENABLE_PREVIEW_FEATURES_SHOWN, true, StorageScope.GLOBAL);
},
isSecondary: true
}]
@@ -61,4 +61,6 @@ export class EnablePreviewFeatures implements IWorkbenchContribution {
})
.then(null, onUnexpectedError);
}
protected abstract getWindowCount(): Promise<number>;
}
@@ -0,0 +1,27 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { AbstractEnablePreviewFeatures } from 'sql/workbench/common/enablePreviewFeatures';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IHostService } from 'vs/workbench/services/host/browser/host';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IElectronService } from 'vs/platform/electron/node/electron';
export class NativeEnablePreviewFeatures extends AbstractEnablePreviewFeatures {
constructor(
@IStorageService storageService: IStorageService,
@INotificationService notificationService: INotificationService,
@IHostService hostService: IHostService,
@IConfigurationService configurationService: IConfigurationService,
@IElectronService private readonly electronService: IElectronService
) {
super(storageService, notificationService, hostService, configurationService);
}
protected getWindowCount(): Promise<number> {
return this.electronService.getWindowCount();
}
}
@@ -138,7 +138,7 @@ registerAction({
await workspaceEditingService.addFolders(folders.map(folder => ({ uri: folder })));
await viewletService.openViewlet(viewletService.getDefaultViewletId(), true);
if (options.forceNewWindow) {
return hostService.openInWindow([{ folderUri: folders[0] }], { forceNewWindow: options.forceNewWindow });
return hostService.openWindow([{ folderUri: folders[0] }], { forceNewWindow: options.forceNewWindow });
}
else {
return hostService.reload();
@@ -4,10 +4,10 @@
*--------------------------------------------------------------------------------------------*/
import { Registry } from 'vs/platform/registry/common/platform';
import { OpenWelcomePageInBrowser } from './openWebsite';
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { BrowserEnablePreviewFeatures } from 'sql/workbench/browser/enablePreviewFeatures';
Registry
.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench)
.registerWorkbenchContribution(OpenWelcomePageInBrowser, LifecyclePhase.Restored);
.registerWorkbenchContribution(BrowserEnablePreviewFeatures, LifecyclePhase.Eventually);
@@ -0,0 +1,13 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Registry } from 'vs/platform/registry/common/platform';
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { NativeEnablePreviewFeatures } from 'sql/workbench/electron-browser/enablePreviewFeatures';
Registry
.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench)
.registerWorkbenchContribution(NativeEnablePreviewFeatures, LifecyclePhase.Eventually);
+1 -1
View File
@@ -14,7 +14,7 @@
"./sql/azdata.d.ts",
"./sql/azdata.proposed.d.ts",
"./vs/base/**/*.ts",
"./vs/platform/**/*.ts",
// "./vs/platform/**/*.ts",
"./sql/base/**/*.ts",
"./sql/editor/**/*.ts",
"./sql/platform/angularEventing/**/*.ts",
+446 -1291
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -9,7 +9,7 @@ import { onUnexpectedError } from 'vs/base/common/errors';
import { IMarkdownString, parseHrefAndDimensions, removeMarkdownEscapes } from 'vs/base/common/htmlContent';
import { defaultGenerator } from 'vs/base/common/idGenerator';
import * as marked from 'vs/base/common/marked/marked';
import * as insane from 'vs/base/common/insane/insane';
import { insane } from 'vs/base/common/insane/insane';
import { parse } from 'vs/base/common/marshalling';
import { cloneAndChange } from 'vs/base/common/objects';
import { escape } from 'vs/base/common/strings';
+37 -23
View File
@@ -152,34 +152,48 @@ export class StandardWheelEvent {
this.deltaX = deltaX;
if (e) {
let e1 = <IWebKitMouseWheelEvent><any>e;
let e2 = <IGeckoMouseWheelEvent><any>e;
if (e.type === 'wheel') {
// vertical delta scroll
if (typeof e1.wheelDeltaY !== 'undefined') {
this.deltaY = e1.wheelDeltaY / 120;
} else if (typeof e2.VERTICAL_AXIS !== 'undefined' && e2.axis === e2.VERTICAL_AXIS) {
this.deltaY = -e2.detail / 3;
} else {
this.deltaY = -e.deltaY / 40;
}
// Modern wheel event
// https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent
const ev = <WheelEvent><unknown>e;
// horizontal delta scroll
if (typeof e1.wheelDeltaX !== 'undefined') {
if (browser.isSafari && platform.isWindows) {
this.deltaX = - (e1.wheelDeltaX / 120);
if (ev.deltaMode === ev.DOM_DELTA_LINE) {
// the deltas are expressed in lines
this.deltaY = -e.deltaY;
this.deltaX = -e.deltaX;
} else {
this.deltaX = e1.wheelDeltaX / 120;
this.deltaY = -e.deltaY / 40;
this.deltaX = -e.deltaX / 40;
}
} else if (typeof e2.HORIZONTAL_AXIS !== 'undefined' && e2.axis === e2.HORIZONTAL_AXIS) {
this.deltaX = -e.detail / 3;
} else {
this.deltaX = -e.deltaX / 40;
}
// Assume a vertical scroll if nothing else worked
if (this.deltaY === 0 && this.deltaX === 0 && e.wheelDelta) {
this.deltaY = e.wheelDelta / 120;
} else {
// Old (deprecated) wheel events
let e1 = <IWebKitMouseWheelEvent><any>e;
let e2 = <IGeckoMouseWheelEvent><any>e;
// vertical delta scroll
if (typeof e1.wheelDeltaY !== 'undefined') {
this.deltaY = e1.wheelDeltaY / 120;
} else if (typeof e2.VERTICAL_AXIS !== 'undefined' && e2.axis === e2.VERTICAL_AXIS) {
this.deltaY = -e2.detail / 3;
}
// horizontal delta scroll
if (typeof e1.wheelDeltaX !== 'undefined') {
if (browser.isSafari && platform.isWindows) {
this.deltaX = - (e1.wheelDeltaX / 120);
} else {
this.deltaX = e1.wheelDeltaX / 120;
}
} else if (typeof e2.HORIZONTAL_AXIS !== 'undefined' && e2.axis === e2.HORIZONTAL_AXIS) {
this.deltaX = -e.detail / 3;
}
// Assume a vertical scroll if nothing else worked
if (this.deltaY === 0 && this.deltaX === 0 && e.wheelDelta) {
this.deltaY = e.wheelDelta / 120;
}
}
}
}
+8 -2
View File
@@ -86,15 +86,21 @@ export class Gesture extends Disposable {
this._register(DomUtils.addDisposableListener(document, 'touchmove', (e: TouchEvent) => this.onTouchMove(e)));
}
public static addTarget(element: HTMLElement): void {
public static addTarget(element: HTMLElement): IDisposable {
if (!Gesture.isTouchDevice()) {
return;
return Disposable.None;
}
if (!Gesture.INSTANCE) {
Gesture.INSTANCE = new Gesture();
}
Gesture.INSTANCE.targets.push(element);
return {
dispose: () => {
Gesture.INSTANCE.targets = Gesture.INSTANCE.targets.filter(t => t !== element);
}
};
}
@memoize
@@ -103,7 +103,7 @@ export class BaseActionViewItem extends Disposable implements IActionViewItem {
render(container: HTMLElement): void {
this.element = container;
Gesture.addTarget(container);
this._register(Gesture.addTarget(container));
const enableDragging = this.options && this.options.draggable;
if (enableDragging) {
+1 -1
View File
@@ -63,7 +63,7 @@ export class Button extends Disposable {
container.appendChild(this._element);
Gesture.addTarget(this._element);
this._register(Gesture.addTarget(this._element));
[DOM.EventType.CLICK, EventType.Tap].forEach(eventType => {
this._register(DOM.addDisposableListener(this._element, eventType, e => {
+1 -1
View File
@@ -83,7 +83,7 @@ export class BaseDropdown extends ActionRunner {
this._register(cleanupFn);
}
Gesture.addTarget(this._label);
this._register(Gesture.addTarget(this._label));
}
get element(): HTMLElement {
+1 -1
View File
@@ -236,7 +236,7 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
this.rowsContainer = document.createElement('div');
this.rowsContainer.className = 'monaco-list-rows';
Gesture.addTarget(this.rowsContainer);
this.disposables.add(Gesture.addTarget(this.rowsContainer));
this.scrollableElement = this.disposables.add(new ScrollableElement(this.rowsContainer, {
alwaysConsumeMouseWheel: true,
+1 -1
View File
@@ -542,7 +542,7 @@ export class MouseController<T> implements IDisposable {
list.onContextMenu(this.onContextMenu, this, this.disposables);
list.onMouseDblClick(this.onDoubleClick, this, this.disposables);
list.onTouchStart(this.onMouseDown, this, this.disposables);
Gesture.addTarget(list.getHTMLElement());
this.disposables.add(Gesture.addTarget(list.getHTMLElement()));
}
list.onMouseClick(this.onPointer, this, this.disposables);
+1 -1
View File
@@ -398,7 +398,7 @@ class BaseMenuActionViewItem extends BaseActionViewItem {
EventHelper.stop(e, true);
this.onClick(e);
}));
}, 50);
}, 100);
this._register(this.runOnceToEnableMouseUp);
}
+2 -2
View File
@@ -238,7 +238,7 @@ export class MenuBar extends Disposable {
}
}));
Gesture.addTarget(buttonElement);
this._register(Gesture.addTarget(buttonElement));
this._register(DOM.addDisposableListener(buttonElement, EventType.Tap, (e: GestureEvent) => {
// Ignore this touch if the menu is touched
if (this.isOpen && this.focusedMenu && this.focusedMenu.holder && DOM.isAncestor(e.initialTarget as HTMLElement, this.focusedMenu.holder)) {
@@ -322,7 +322,7 @@ export class MenuBar extends Disposable {
}
}));
Gesture.addTarget(buttonElement);
this._register(Gesture.addTarget(buttonElement));
this._register(DOM.addDisposableListener(buttonElement, EventType.Tap, (e: GestureEvent) => {
// Ignore this touch if the menu is touched
if (this.isOpen && this.focusedMenu && this.focusedMenu.holder && DOM.isAncestor(e.initialTarget as HTMLElement, this.focusedMenu.holder)) {
+1 -1
View File
@@ -1,6 +1,6 @@
@font-face {
font-family: "octicons";
src: url("./octicons.ttf?628f71ee09945d25ba5fceb0c17f7b0f") format("truetype");
src: url("./octicons.ttf?1829db8570ee0fa5a4bef3bb41d5f62e") format("truetype");
}
.octicon, .mega-octicon {
+1 -1
View File
@@ -139,7 +139,7 @@ export class Sash extends Disposable {
this._register(domEvent(this.el, 'mousedown')(this.onMouseDown, this));
this._register(domEvent(this.el, 'dblclick')(this.onMouseDoubleClick, this));
Gesture.addTarget(this.el);
this._register(Gesture.addTarget(this.el));
this._register(domEvent(this.el, EventType.Start)(this.onTouchStart, this));
if (isIPad) {
+4 -1
View File
@@ -23,7 +23,10 @@ export class MarkdownString implements IMarkdownString {
appendText(value: string): MarkdownString {
// escape markdown syntax tokens: http://daringfireball.net/projects/markdown/syntax#backslash
this.value += value.replace(/[\\`*_{}[\]()#+\-.!]/g, '\\$&');
this.value += value
.replace(/[\\`*_{}[\]()#+\-.!]/g, '\\$&')
.replace('\n', '\n\n');
return this;
}
+1 -7
View File
@@ -3,11 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export as namespace insane;
export = insane;
declare function insane(
export function insane(
html: string,
options?: {
readonly allowedSchemes?: readonly string[],
@@ -16,5 +12,3 @@ declare function insane(
},
strict?: boolean,
): string;
declare namespace insane { }
+7 -11
View File
@@ -21,11 +21,7 @@ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
// ESM-comment-begin
let __insane_exports;
// ESM-comment-end
let __insane_func;
(function () { function r(e, n, t) { function o(i, f) { if (!n[i]) { if (!e[i]) { var c = "function" == typeof require && require; if (!f && c) return c(i, !0); if (u) return u(i, !0); var a = new Error("Cannot find module '" + i + "'"); throw a.code = "MODULE_NOT_FOUND", a } var p = n[i] = { exports: {} }; e[i][0].call(p.exports, function (r) { var n = e[i][1][r]; return o(n || r) }, p, p.exports, r, e, n, t) } return n[i].exports } for (var u = "function" == typeof require && require, i = 0; i < t.length; i++)o(t[i]); return o } return r })()({
1: [function (require, module, exports) {
@@ -92,7 +88,7 @@ let __insane_exports;
insane.defaults = defaults;
module.exports = insane;
__insane_exports = insane;
__insane_func = insane;
}, { "./defaults": 2, "./parser": 7, "./sanitizer": 8, "assignment": 6, "he": 9 }], 5: [function (require, module, exports) {
'use strict';
@@ -469,10 +465,10 @@ let __insane_exports;
}, {}]
}, {}, [4]);
// BEGIN MONACOCHANGE
// __marked_exports = marked;
// }).call(this);
// ESM-comment-begin
define(function() { return __insane_exports; });
define(function() { return { insane: __insane_func }; });
// ESM-comment-end
// ESM-uncomment-begin
// export var insane = __insane_func;
// ESM-uncomment-end
+4 -1
View File
@@ -92,7 +92,10 @@ class RemoteAuthoritiesImpl {
return this._delegate(uri);
}
const authority = uri.authority;
const host = this._hosts[authority];
let host = this._hosts[authority];
if (host.indexOf(':') !== -1) {
host = `[${host}]`;
}
const port = this._ports[authority];
const connectionToken = this._connectionTokens[authority];
return URI.from({
@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Menu, MenuItem, BrowserWindow, ipcMain, IpcMainEvent } from 'electron';
import { Menu, MenuItem, BrowserWindow, ipcMain, Event as IpcMainEvent } from 'electron';
import { ISerializableContextMenuItem, CONTEXT_MENU_CLOSE_CHANNEL, CONTEXT_MENU_CHANNEL, IPopupOptions } from 'vs/base/parts/contextmenu/common/contextmenu';
export function registerContextMenuListener(): void {
+3 -1
View File
@@ -437,6 +437,7 @@ export class TreeView extends HeightMap {
private shouldInvalidateDropReaction: boolean;
private currentDropTargets: ViewItem[] | null = null;
private currentDropDisposable: Lifecycle.IDisposable = Lifecycle.Disposable.None;
private gestureDisposable: Lifecycle.IDisposable = Lifecycle.Disposable.None;
private dragAndDropScrollInterval: number | null = null;
private dragAndDropScrollTimeout: number | null = null;
private dragAndDropMouseY: number | null = null;
@@ -523,7 +524,7 @@ export class TreeView extends HeightMap {
this.wrapper.style.msTouchAction = 'none';
this.wrapper.style.msContentZooming = 'none';
} else {
Touch.Gesture.addTarget(this.wrapper);
this.gestureDisposable = Touch.Gesture.addTarget(this.wrapper);
}
this.rowsContainer = document.createElement('div');
@@ -1681,6 +1682,7 @@ export class TreeView extends HeightMap {
if (this.context.cache) {
this.context.cache.dispose();
}
this.gestureDisposable.dispose();
super.dispose();
}
@@ -0,0 +1,19 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { MarkdownString } from 'vs/base/common/htmlContent';
suite('markdownString', () => {
test('escape', () => {
const mds = new MarkdownString();
mds.appendText('# foo\n*bar*');
assert.equal(mds.value, '\\# foo\n\n\\*bar\\*');
});
});
@@ -27,6 +27,7 @@ function getWorker(workerId: string, label: string): Worker | Promise<Worker> {
throw new Error(`You must define a function MonacoEnvironment.getWorkerUrl or MonacoEnvironment.getWorker`);
}
// ESM-comment-begin
export function getWorkerBootstrapUrl(scriptPath: string, label: string): string {
if (/^(http:)|(https:)|(file:)/.test(scriptPath)) {
const currentUrl = String(window.location);
@@ -43,6 +44,7 @@ export function getWorkerBootstrapUrl(scriptPath: string, label: string): string
}
return scriptPath + '#' + label;
}
// ESM-comment-end
function isPromiseLike<T>(obj: any): obj is PromiseLike<T> {
if (typeof obj.then === 'function') {
+10 -6
View File
@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { app, ipcMain as ipc, systemPreferences, shell, Event, contentTracing, protocol, powerMonitor, IpcMainEvent } from 'electron';
import { app, ipcMain as ipc, systemPreferences, shell, Event, contentTracing, protocol, powerMonitor, Event as IpcMainEvent, BrowserWindow } from 'electron';
import { IProcessEnvironment, isWindows, isMacintosh } from 'vs/base/common/platform';
import { WindowsManager } from 'vs/code/electron-main/windows';
import { OpenContext, IWindowOpenable } from 'vs/platform/windows/common/windows';
@@ -78,6 +78,8 @@ import { IElectronService } from 'vs/platform/electron/node/electron';
import { ElectronMainService } from 'vs/platform/electron/electron-main/electronMainService';
import { ISharedProcessMainService, SharedProcessMainService } from 'vs/platform/ipc/electron-main/sharedProcessMainService';
import { assign } from 'vs/base/common/objects';
import { IDialogMainService, DialogMainService } from 'vs/platform/dialogs/electron-main/dialogs';
import { withNullAsUndefined } from 'vs/base/common/types';
export class CodeApplication extends Disposable {
@@ -85,6 +87,7 @@ export class CodeApplication extends Disposable {
private static readonly TRUE_MACHINE_ID_KEY = 'telemetry.trueMachineId';
private windowsMainService: IWindowsMainService | undefined;
private dialogMainService: IDialogMainService | undefined;
constructor(
private readonly mainIpcServer: Server,
@@ -381,8 +384,7 @@ export class CodeApplication extends Disposable {
}
// Setup Auth Handler
const authHandler = appInstantiationService.createInstance(ProxyAuthHandler);
this._register(authHandler);
this._register(new ProxyAuthHandler());
// Open Windows
const windows = appInstantiationService.invokeFunction(accessor => this.openFirstWindow(accessor, electronIpcServer, sharedProcessClient));
@@ -449,6 +451,7 @@ export class CodeApplication extends Disposable {
}
services.set(IWindowsMainService, new SyncDescriptor(WindowsManager, [machineId, this.userEnv]));
services.set(IDialogMainService, new SyncDescriptor(DialogMainService));
services.set(ISharedProcessMainService, new SyncDescriptor(SharedProcessMainService, [sharedProcess]));
services.set(ILaunchMainService, new SyncDescriptor(LaunchMainService));
@@ -503,13 +506,13 @@ export class CodeApplication extends Disposable {
contentTracing.stopRecording(join(homedir(), `${product.applicationName}-${Math.random().toString(16).slice(-4)}.trace.txt`), path => {
if (!timeout) {
if (this.windowsMainService) {
this.windowsMainService.showMessageBox({
if (this.dialogMainService) {
this.dialogMainService.showMessageBox({
type: 'info',
message: localize('trace.message', "Successfully created trace."),
detail: localize('trace.detail', "Please create an issue and manually attach the following file:\n{0}", path),
buttons: [localize('trace.ok', "Ok")]
}, this.windowsMainService.getLastActiveWindow());
}, withNullAsUndefined(BrowserWindow.getFocusedWindow()));
}
} else {
this.logService.info(`Tracing: data recorded (after 30s timeout) to ${path}`);
@@ -580,6 +583,7 @@ export class CodeApplication extends Disposable {
// Propagate to clients
const windowsMainService = this.windowsMainService = accessor.get(IWindowsMainService);
this.dialogMainService = accessor.get(IDialogMainService);
// Create a URL handler to open file URIs in the active window
const environmentService = accessor.get(IEnvironmentService);
+12 -15
View File
@@ -4,8 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { localize } from 'vs/nls';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows';
import { Disposable } from 'vs/base/common/lifecycle';
import { Event } from 'vs/base/common/event';
import { BrowserWindow, app, AuthInfo, WebContents, Event as ElectronEvent } from 'electron';
@@ -22,18 +21,21 @@ type Credentials = {
password: string;
};
export class ProxyAuthHandler {
export class ProxyAuthHandler extends Disposable {
_serviceBrand: undefined;
private retryCount = 0;
private disposables: IDisposable[] = [];
constructor(
@IWindowsMainService private readonly windowsMainService: IWindowsMainService
) {
constructor() {
super();
this.registerListeners();
}
private registerListeners(): void {
const onLogin = Event.fromNodeEventEmitter<LoginEvent>(app, 'login', (event, webContents, req, authInfo, cb) => ({ event, webContents, req, authInfo, cb }));
onLogin(this.onLogin, this, this.disposables);
this._register(onLogin(this.onLogin, this));
}
private onLogin({ event, authInfo, cb }: LoginEvent): void {
@@ -61,10 +63,9 @@ export class ProxyAuthHandler {
}
};
const focusedWindow = this.windowsMainService.getFocusedWindow();
const focusedWindow = BrowserWindow.getFocusedWindow();
if (focusedWindow) {
opts.parent = focusedWindow.win;
opts.parent = focusedWindow;
opts.modal = true;
}
@@ -89,8 +90,4 @@ export class ProxyAuthHandler {
win.close();
});
}
dispose(): void {
this.disposables = dispose(this.disposables);
}
}
+1 -1
View File
@@ -7,7 +7,7 @@ import { assign } from 'vs/base/common/objects';
import { memoize } from 'vs/base/common/decorators';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { BrowserWindow, ipcMain } from 'electron';
import { ISharedProcess } from 'vs/platform/windows/electron-main/windows';
import { ISharedProcess } from 'vs/platform/ipc/electron-main/sharedProcessMainService';
import { Barrier } from 'vs/base/common/async';
import { ILogService } from 'vs/platform/log/common/log';
import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
+1 -1
View File
@@ -561,7 +561,7 @@ export class CodeWindow extends Disposable implements ICodeWindow {
autoDetectHighContrast = false;
}
windowConfiguration.highContrast = isWindows && autoDetectHighContrast && systemPreferences.isInvertedColorScheme();
windowConfiguration.accessibilitySupport = app.accessibilitySupportEnabled;
windowConfiguration.accessibilitySupport = app.isAccessibilitySupportEnabled();
// Title style related
windowConfiguration.maximized = this._win.isMaximized();
+85 -320
View File
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as fs from 'fs';
import { basename, normalize, join, dirname } from 'vs/base/common/path';
import { basename, normalize, join, } from 'vs/base/common/path';
import { localize } from 'vs/nls';
import * as arrays from 'vs/base/common/arrays';
import { assign, mixin } from 'vs/base/common/objects';
@@ -13,34 +13,36 @@ import { IEmptyWindowBackupInfo } from 'vs/platform/backup/node/backup';
import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment';
import { IStateService } from 'vs/platform/state/node/state';
import { CodeWindow, defaultWindowState } from 'vs/code/electron-main/window';
import { ipcMain as ipc, screen, BrowserWindow, dialog, systemPreferences, FileFilter, shell, MessageBoxReturnValue, MessageBoxOptions, SaveDialogOptions, SaveDialogReturnValue, OpenDialogOptions, OpenDialogReturnValue, Display } from 'electron';
import { ipcMain as ipc, screen, BrowserWindow, systemPreferences, MessageBoxOptions, Display } from 'electron';
import { parseLineAndColumnAware } from 'vs/code/node/paths';
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';
import { IWindowSettings, OpenContext, IPath, IWindowConfiguration, IPathsToWaitFor, isFileToOpen, isWorkspaceToOpen, isFolderToOpen, IWindowOpenable, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows';
import { IWindowSettings, OpenContext, IPath, IWindowConfiguration, IPathsToWaitFor, isFileToOpen, isWorkspaceToOpen, isFolderToOpen, IWindowOpenable, IOpenEmptyWindowOptions, IAddFoldersRequest } from 'vs/platform/windows/common/windows';
import { INativeOpenDialogOptions } from 'vs/platform/dialogs/node/dialogs';
import { getLastActiveWindow, findBestWindowOrFolderForFile, findWindowOnWorkspace, findWindowOnExtensionDevelopmentPath, findWindowOnWorkspaceOrFolderUri } from 'vs/code/node/windowsFinder';
import { getLastActiveWindow, findBestWindowOrFolderForFile, findWindowOnWorkspace, findWindowOnExtensionDevelopmentPath, findWindowOnWorkspaceOrFolderUri } from 'vs/platform/windows/node/window';
import { Event as CommonEvent, Emitter } from 'vs/base/common/event';
import product from 'vs/platform/product/common/product';
import { ITelemetryService, ITelemetryData } from 'vs/platform/telemetry/common/telemetry';
import { IWindowsMainService, IOpenConfiguration, IWindowsCountChangedEvent, ICodeWindow, IWindowState as ISingleWindowState, WindowMode } from 'vs/platform/windows/electron-main/windows';
import { IWorkspacesHistoryMainService } from 'vs/platform/workspaces/electron-main/workspacesHistoryMainService';
import { IProcessEnvironment, isMacintosh, isWindows } from 'vs/base/common/platform';
import { IWorkspaceIdentifier, WORKSPACE_FILTER, isSingleFolderWorkspaceIdentifier, hasWorkspaceFileExtension, IEnterWorkspaceResult, IRecent } from 'vs/platform/workspaces/common/workspaces';
import { IWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, hasWorkspaceFileExtension, IRecent } from 'vs/platform/workspaces/common/workspaces';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { mnemonicButtonLabel } from 'vs/base/common/labels';
import { Schemas } from 'vs/base/common/network';
import { normalizeNFC } from 'vs/base/common/normalization';
import { URI } from 'vs/base/common/uri';
import { Queue } from 'vs/base/common/async';
import { exists, dirExists } from 'vs/base/node/pfs';
import { getComparisonKey, isEqual, normalizePath, basename as resourcesBasename, originalFSPath, hasTrailingPathSeparator, removeTrailingPathSeparator } from 'vs/base/common/resources';
import { dirExists } from 'vs/base/node/pfs';
import { getComparisonKey, isEqual, normalizePath, originalFSPath, hasTrailingPathSeparator, removeTrailingPathSeparator } from 'vs/base/common/resources';
import { getRemoteAuthority } from 'vs/platform/remote/common/remoteHosts';
import { restoreWindowsState, WindowsStateStorageData, getWindowsStateStoreData } from 'vs/code/electron-main/windowsStateStorage';
import { getWorkspaceIdentifier, IWorkspacesMainService } from 'vs/platform/workspaces/electron-main/workspacesMainService';
import { once } from 'vs/base/common/functional';
import { Disposable } from 'vs/base/common/lifecycle';
import { IDialogMainService } from 'vs/platform/dialogs/electron-main/dialogs';
import { withNullAsUndefined } from 'vs/base/common/types';
import { isWindowsDriveLetter, toSlashes } from 'vs/base/common/extpath';
import { CharCode } from 'vs/base/common/charCode';
const enum WindowError {
UNRESPONSIVE = 1,
@@ -167,9 +169,6 @@ export class WindowsManager extends Disposable implements IWindowsMainService {
private readonly windowsState: IWindowsState;
private lastClosedWindowState?: IWindowState;
private readonly dialogs: Dialogs;
private readonly workspacesManager: WorkspacesManager;
private readonly _onWindowReady = this._register(new Emitter<ICodeWindow>());
readonly onWindowReady: CommonEvent<ICodeWindow> = this._onWindowReady.event;
@@ -194,7 +193,8 @@ export class WindowsManager extends Disposable implements IWindowsMainService {
@IConfigurationService private readonly configurationService: IConfigurationService,
@IWorkspacesHistoryMainService private readonly workspacesHistoryMainService: IWorkspacesHistoryMainService,
@IWorkspacesMainService private readonly workspacesMainService: IWorkspacesMainService,
@IInstantiationService private readonly instantiationService: IInstantiationService
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IDialogMainService private readonly dialogMainService: IDialogMainService
) {
super();
@@ -203,9 +203,6 @@ export class WindowsManager extends Disposable implements IWindowsMainService {
this.windowsState.openedWindows = [];
}
this.dialogs = new Dialogs(stateService, this);
this.workspacesManager = new WorkspacesManager(workspacesMainService, backupMainService, this);
this.lifecycleMainService.when(LifecycleMainPhase.Ready).then(() => this.registerListeners());
this.lifecycleMainService.when(LifecycleMainPhase.AfterWindowOpen).then(() => this.installWindowsMutex());
}
@@ -260,6 +257,11 @@ export class WindowsManager extends Disposable implements IWindowsMainService {
this.lastClosedWindowState = undefined;
}
});
// Signal a window is ready after having entered a workspace
this._register(this.workspacesMainService.onWorkspaceEntered(event => {
this._onWindowReady.fire(event.window);
}));
}
// Note that onBeforeShutdown() and onBeforeWindowClose() are fired in different order depending on the OS:
@@ -380,12 +382,6 @@ export class WindowsManager extends Disposable implements IWindowsMainService {
};
}
async openExternal(url: string): Promise<boolean> {
shell.openExternal(url);
return true;
}
open(openConfig: IOpenConfiguration): ICodeWindow[] {
this.logService.trace('windowsManager#open');
openConfig = this.validateOpenConfig(openConfig);
@@ -776,7 +772,9 @@ export class WindowsManager extends Disposable implements IWindowsMainService {
private doAddFoldersToExistingWindow(window: ICodeWindow, foldersToAdd: URI[]): ICodeWindow {
window.focus(); // make sure window has focus
window.sendWhenReady('vscode:addFolders', { foldersToAdd });
const request: IAddFoldersRequest = { foldersToAdd };
window.sendWhenReady('vscode:addFolders', request);
return window;
}
@@ -862,7 +860,8 @@ export class WindowsManager extends Disposable implements IWindowsMainService {
path.label = pathToOpen.label;
pathsToOpen.push(path);
} else {
const uri = resourceFromURIToOpen(pathToOpen);
const uri = this.resourceFromURIToOpen(pathToOpen);
// Warn about the invalid URI or path
let message, detail;
if (uri.scheme === Schemas.file) {
@@ -881,7 +880,7 @@ export class WindowsManager extends Disposable implements IWindowsMainService {
noLink: true
};
this.dialogs.showMessageBox(options, this.getFocusedWindow());
this.dialogMainService.showMessageBox(options, withNullAsUndefined(BrowserWindow.getFocusedWindow()));
}
}
return pathsToOpen;
@@ -1011,10 +1010,12 @@ export class WindowsManager extends Disposable implements IWindowsMainService {
this.logService.error(`Invalid URI input string, scheme missing: ${arg}`);
return undefined;
}
return uri;
} catch (e) {
this.logService.error(`Invalid URI input string: ${arg}, ${e.message}`);
}
return undefined;
}
@@ -1023,7 +1024,7 @@ export class WindowsManager extends Disposable implements IWindowsMainService {
return undefined;
}
let uri = resourceFromURIToOpen(toOpen);
let uri = this.resourceFromURIToOpen(toOpen);
if (uri.scheme === Schemas.file) {
return this.parsePath(uri.fsPath, options, isFileToOpen(toOpen));
}
@@ -1050,6 +1051,7 @@ export class WindowsManager extends Disposable implements IWindowsMainService {
remoteAuthority
};
}
return {
fileUri: uri,
remoteAuthority
@@ -1071,6 +1073,18 @@ export class WindowsManager extends Disposable implements IWindowsMainService {
};
}
private resourceFromURIToOpen(openable: IWindowOpenable): URI {
if (isWorkspaceToOpen(openable)) {
return openable.workspaceUri;
}
if (isFolderToOpen(openable)) {
return openable.folderUri;
}
return openable.fileUri;
}
private parsePath(anyPath: string, options: IPathParseOptions, forceOpenWorkspaceAsFile?: boolean): IPathToOpen | undefined {
if (!anyPath) {
return undefined;
@@ -1086,11 +1100,36 @@ export class WindowsManager extends Disposable implements IWindowsMainService {
anyPath = parsedPath.path;
}
// open remote if either specified in the cli even if it is a local file. TODO@aeschli: Future idea: resolve in remote host context.
// open remote if either specified in the cli even if it is a local file.
const remoteAuthority = options.remoteAuthority;
const candidate = normalize(anyPath);
if (remoteAuthority) {
// assume it's a folder or workspace file
const first = anyPath.charCodeAt(0);
// make absolute
if (first !== CharCode.Slash) {
if (isWindowsDriveLetter(first) && anyPath.charCodeAt(anyPath.charCodeAt(1)) === CharCode.Colon) {
anyPath = toSlashes(anyPath);
}
anyPath = '/' + anyPath;
}
const uri = URI.from({ scheme: Schemas.vscodeRemote, authority: remoteAuthority, path: anyPath });
if (hasWorkspaceFileExtension(anyPath)) {
if (forceOpenWorkspaceAsFile) {
return { fileUri: uri, remoteAuthority };
}
return { workspace: getWorkspaceIdentifier(uri), remoteAuthority };
}
return { folderUri: uri, remoteAuthority };
}
let candidate = normalize(anyPath);
try {
const candidateStat = fs.statSync(candidate);
if (candidateStat.isFile()) {
@@ -1185,7 +1224,7 @@ export class WindowsManager extends Disposable implements IWindowsMainService {
return { openFolderInNewWindow: !!openFolderInNewWindow, openFilesInNewWindow };
}
openExtensionDevelopmentHostWindow(extensionDevelopmentPath: string[], openConfig: IOpenConfiguration): void {
openExtensionDevelopmentHostWindow(extensionDevelopmentPath: string[], openConfig: IOpenConfiguration): ICodeWindow[] {
// Reload an existing extension development host window on the same path
// We currently do not allow more than one extension development window
@@ -1195,7 +1234,7 @@ export class WindowsManager extends Disposable implements IWindowsMainService {
this.reload(existingWindow, openConfig.cli);
existingWindow.focus(); // make sure it gets focus and is restored
return;
return [existingWindow];
}
let folderUris = openConfig.cli['folder-uri'] || [];
let fileUris = openConfig.cli['file-uri'] || [];
@@ -1286,7 +1325,8 @@ export class WindowsManager extends Disposable implements IWindowsMainService {
noRecentEntry: true,
waitMarkerFileURI: openConfig.waitMarkerFileURI
};
this.open(openArgs);
return this.open(openArgs);
}
private openInBrowserWindow(options: IOpenBrowserWindowOptions): ICodeWindow {
@@ -1580,23 +1620,6 @@ export class WindowsManager extends Disposable implements IWindowsMainService {
});
}
async enterWorkspace(win: ICodeWindow, path: URI): Promise<IEnterWorkspaceResult | undefined> {
const result = await this.workspacesManager.enterWorkspace(win, path);
return result ? this.doEnterWorkspace(win, result) : undefined;
}
private doEnterWorkspace(win: ICodeWindow, result: IEnterWorkspaceResult): IEnterWorkspaceResult {
// Mark as recently opened
this.workspacesHistoryMainService.addRecentlyOpened([{ workspace: result.workspace }]);
// Trigger Eevent to indicate load of workspace into window
this._onWindowReady.fire(win);
return result;
}
focusLastActive(cli: ParsedArgs, context: OpenContext): ICodeWindow {
const lastActive = this.getLastActiveWindow();
if (lastActive) {
@@ -1613,7 +1636,7 @@ export class WindowsManager extends Disposable implements IWindowsMainService {
return getLastActiveWindow(WindowsManager.WINDOWS);
}
getLastActiveWindowForAuthority(remoteAuthority: string | undefined): ICodeWindow | undefined {
private getLastActiveWindowForAuthority(remoteAuthority: string | undefined): ICodeWindow | undefined {
return getLastActiveWindow(WindowsManager.WINDOWS.filter(window => window.remoteAuthority === remoteAuthority));
}
@@ -1623,13 +1646,11 @@ export class WindowsManager extends Disposable implements IWindowsMainService {
if (cli && (cli.remote !== remote)) {
cli = { ...cli, remote };
}
const forceReuseWindow = options && options.reuse;
const forceNewWindow = !forceReuseWindow;
return this.open({ context, cli, forceEmpty: true, forceNewWindow, forceReuseWindow });
}
openNewTabbedWindow(context: OpenContext): ICodeWindow[] {
return this.open({ context, cli: this.environmentService.args, forceNewTabbedWindow: true, forceEmpty: true });
const forceReuseWindow = options && options.forceReuseWindow;
const forceNewWindow = !forceReuseWindow;
return this.open({ context, cli, forceEmpty: true, forceNewWindow, forceReuseWindow });
}
waitForWindowCloseOrLoad(windowId: number): Promise<void> {
@@ -1666,7 +1687,7 @@ export class WindowsManager extends Disposable implements IWindowsMainService {
}
}
getFocusedWindow(): ICodeWindow | undefined {
private getFocusedWindow(): ICodeWindow | undefined {
const win = BrowserWindow.getFocusedWindow();
if (win) {
return this.getWindowById(win.id);
@@ -1710,14 +1731,14 @@ export class WindowsManager extends Disposable implements IWindowsMainService {
}
// Show Dialog
this.dialogs.showMessageBox({
this.dialogMainService.showMessageBox({
title: product.nameLong,
type: 'warning',
buttons: [mnemonicButtonLabel(localize({ key: 'reopen', comment: ['&& denotes a mnemonic'] }, "&&Reopen")), mnemonicButtonLabel(localize({ key: 'wait', comment: ['&& denotes a mnemonic'] }, "&&Keep Waiting")), mnemonicButtonLabel(localize({ key: 'close', comment: ['&& denotes a mnemonic'] }, "&&Close"))],
message: localize('appStalled', "The window is no longer responding"),
detail: localize('appStalledDetail', "You can reopen or close the window or keep waiting."),
noLink: true
}, window).then(result => {
}, window.win).then(result => {
if (!window.win) {
return; // Return early if the window has been going down already
}
@@ -1733,14 +1754,14 @@ export class WindowsManager extends Disposable implements IWindowsMainService {
// Crashed
else {
this.dialogs.showMessageBox({
this.dialogMainService.showMessageBox({
title: product.nameLong,
type: 'warning',
buttons: [mnemonicButtonLabel(localize({ key: 'reopen', comment: ['&& denotes a mnemonic'] }, "&&Reopen")), mnemonicButtonLabel(localize({ key: 'close', comment: ['&& denotes a mnemonic'] }, "&&Close"))],
message: localize('appCrashed', "The window has crashed"),
detail: localize('appCrashedDetail', "We are sorry for the inconvenience! You can reopen the window to continue where you left off."),
noLink: true
}, window).then(result => {
}, window.win).then(result => {
if (!window.win) {
return; // Return early if the window has been going down already
}
@@ -1770,8 +1791,7 @@ export class WindowsManager extends Disposable implements IWindowsMainService {
}
async pickFileFolderAndOpen(options: INativeOpenDialogOptions, win?: ICodeWindow): Promise<void> {
const title = localize('open', "Open");
const paths = await this.dialogs.pick({ ...options, pickFolders: true, pickFiles: true, title });
const paths = await this.dialogMainService.pickFileFolder(options);
if (paths) {
this.sendPickerTelemetry(paths, options.telemetryEventName || 'openFileFolder', options.telemetryExtraData);
const urisToOpen = await Promise.all(paths.map(async path => {
@@ -1790,8 +1810,7 @@ export class WindowsManager extends Disposable implements IWindowsMainService {
}
async pickFolderAndOpen(options: INativeOpenDialogOptions, win?: ICodeWindow): Promise<void> {
const title = localize('openFolder', "Open Folder");
const paths = await this.dialogs.pick({ ...options, pickFolders: true, title });
const paths = await this.dialogMainService.pickFolder(options);
if (paths) {
this.sendPickerTelemetry(paths, options.telemetryEventName || 'openFolder', options.telemetryExtraData);
this.open({
@@ -1805,8 +1824,7 @@ export class WindowsManager extends Disposable implements IWindowsMainService {
}
async pickFileAndOpen(options: INativeOpenDialogOptions, win?: ICodeWindow): Promise<void> {
const title = localize('openFile', "Open File");
const paths = await this.dialogs.pick({ ...options, pickFiles: true, title });
const paths = await this.dialogMainService.pickFile(options);
if (paths) {
this.sendPickerTelemetry(paths, options.telemetryEventName || 'openFile', options.telemetryExtraData);
this.open({
@@ -1820,10 +1838,7 @@ export class WindowsManager extends Disposable implements IWindowsMainService {
}
async pickWorkspaceAndOpen(options: INativeOpenDialogOptions, win?: ICodeWindow): Promise<void> {
const title = localize('openWorkspaceTitle', "Open Workspace");
const buttonLabel = mnemonicButtonLabel(localize({ key: 'openWorkspace', comment: ['&& denotes a mnemonic'] }, "&&Open"));
const filters = WORKSPACE_FILTER;
const paths = await this.dialogs.pick({ ...options, pickFiles: true, title, filters, buttonLabel });
const paths = await this.dialogMainService.pickWorkspace(options);
if (paths) {
this.sendPickerTelemetry(paths, options.telemetryEventName || 'openWorkspace', options.telemetryExtraData);
this.open({
@@ -1838,7 +1853,6 @@ export class WindowsManager extends Disposable implements IWindowsMainService {
}
private sendPickerTelemetry(paths: string[], telemetryEventName: string, telemetryExtraData?: ITelemetryData) {
const numberOfPaths = paths ? paths.length : 0;
// Telemetry
@@ -1850,18 +1864,6 @@ export class WindowsManager extends Disposable implements IWindowsMainService {
});
}
showMessageBox(options: MessageBoxOptions, win?: ICodeWindow): Promise<MessageBoxReturnValue> {
return this.dialogs.showMessageBox(options, win);
}
showSaveDialog(options: SaveDialogOptions, win?: ICodeWindow): Promise<SaveDialogReturnValue> {
return this.dialogs.showSaveDialog(options, win);
}
showOpenDialog(options: OpenDialogOptions, win?: ICodeWindow): Promise<OpenDialogReturnValue> {
return this.dialogs.showOpenDialog(options, win);
}
quit(): void {
// If the user selected to exit from an extension development host window, do not quit, but just
@@ -1879,240 +1881,3 @@ export class WindowsManager extends Disposable implements IWindowsMainService {
}
}
}
interface IInternalNativeOpenDialogOptions extends INativeOpenDialogOptions {
pickFolders?: boolean;
pickFiles?: boolean;
title: string;
buttonLabel?: string;
filters?: FileFilter[];
}
class Dialogs {
private static readonly workingDirPickerStorageKey = 'pickerWorkingDir';
private readonly mapWindowToDialogQueue: Map<number, Queue<void>>;
private readonly noWindowDialogQueue: Queue<void>;
constructor(
private readonly stateService: IStateService,
private readonly windowsMainService: IWindowsMainService
) {
this.mapWindowToDialogQueue = new Map<number, Queue<void>>();
this.noWindowDialogQueue = new Queue<void>();
}
async pick(options: IInternalNativeOpenDialogOptions, win?: ICodeWindow): Promise<string[] | undefined> {
// Ensure dialog options
const dialogOptions: OpenDialogOptions = {
title: options.title,
buttonLabel: options.buttonLabel,
filters: options.filters
};
// Ensure defaultPath
dialogOptions.defaultPath = options.defaultPath || this.stateService.getItem<string>(Dialogs.workingDirPickerStorageKey);
// Ensure properties
if (typeof options.pickFiles === 'boolean' || typeof options.pickFolders === 'boolean') {
dialogOptions.properties = undefined; // let it override based on the booleans
if (options.pickFiles && options.pickFolders) {
dialogOptions.properties = ['multiSelections', 'openDirectory', 'openFile', 'createDirectory'];
}
}
if (!dialogOptions.properties) {
dialogOptions.properties = ['multiSelections', options.pickFolders ? 'openDirectory' : 'openFile', 'createDirectory'];
}
if (isMacintosh) {
dialogOptions.properties.push('treatPackageAsDirectory'); // always drill into .app files
}
// Show Dialog
const windowToUse = win || this.windowsMainService.getFocusedWindow();
const result = await this.showOpenDialog(dialogOptions, windowToUse);
if (result && result.filePaths && result.filePaths.length > 0) {
// Remember path in storage for next time
this.stateService.setItem(Dialogs.workingDirPickerStorageKey, dirname(result.filePaths[0]));
return result.filePaths;
}
return undefined; // {{SQL CARBON EDIT}} @anthonydresser strict-null-check
}
private getDialogQueue(window?: ICodeWindow): Queue<any> {
if (!window) {
return this.noWindowDialogQueue;
}
let windowDialogQueue = this.mapWindowToDialogQueue.get(window.id);
if (!windowDialogQueue) {
windowDialogQueue = new Queue<any>();
this.mapWindowToDialogQueue.set(window.id, windowDialogQueue);
}
return windowDialogQueue;
}
showMessageBox(options: MessageBoxOptions, window?: ICodeWindow): Promise<MessageBoxReturnValue> {
return this.getDialogQueue(window).queue(async () => {
if (window) {
return dialog.showMessageBox(window.win, options);
}
return dialog.showMessageBox(options);
});
}
showSaveDialog(options: SaveDialogOptions, window?: ICodeWindow): Promise<SaveDialogReturnValue> {
function normalizePath(path: string | undefined): string | undefined {
if (path && isMacintosh) {
path = normalizeNFC(path); // normalize paths returned from the OS
}
return path;
}
return this.getDialogQueue(window).queue(async () => {
let result: SaveDialogReturnValue;
if (window) {
result = await dialog.showSaveDialog(window.win, options);
} else {
result = await dialog.showSaveDialog(options);
}
result.filePath = normalizePath(result.filePath);
return result;
});
}
showOpenDialog(options: OpenDialogOptions, window?: ICodeWindow): Promise<OpenDialogReturnValue> {
function normalizePaths(paths: string[] | undefined): string[] | undefined {
if (paths && paths.length > 0 && isMacintosh) {
paths = paths.map(path => normalizeNFC(path)); // normalize paths returned from the OS
}
return paths;
}
return this.getDialogQueue(window).queue(async () => {
// Ensure the path exists (if provided)
if (options.defaultPath) {
const pathExists = await exists(options.defaultPath);
if (!pathExists) {
options.defaultPath = undefined;
}
}
// Show dialog
let result: OpenDialogReturnValue;
if (window) {
result = await dialog.showOpenDialog(window.win, options);
} else {
result = await dialog.showOpenDialog(options);
}
result.filePaths = normalizePaths(result.filePaths);
return result;
});
}
}
class WorkspacesManager {
constructor(
private readonly workspacesMainService: IWorkspacesMainService,
private readonly backupMainService: IBackupMainService,
private readonly windowsMainService: IWindowsMainService,
) { }
async enterWorkspace(window: ICodeWindow, path: URI): Promise<IEnterWorkspaceResult | null> {
if (!window || !window.win || !window.isReady) {
return null; // return early if the window is not ready or disposed
}
const isValid = await this.isValidTargetWorkspacePath(window, path);
if (!isValid) {
return null; // return early if the workspace is not valid
}
return this.doOpenWorkspace(window, getWorkspaceIdentifier(path));
}
private async isValidTargetWorkspacePath(window: ICodeWindow, path?: URI): Promise<boolean> {
if (!path) {
return true;
}
if (window.openedWorkspace && isEqual(window.openedWorkspace.configPath, path)) {
return false; // window is already opened on a workspace with that path
}
// Prevent overwriting a workspace that is currently opened in another window
if (findWindowOnWorkspace(this.windowsMainService.getWindows(), getWorkspaceIdentifier(path))) {
const options: MessageBoxOptions = {
title: product.nameLong,
type: 'info',
buttons: [localize('ok', "OK")],
message: localize('workspaceOpenedMessage', "Unable to save workspace '{0}'", resourcesBasename(path)),
detail: localize('workspaceOpenedDetail', "The workspace is already opened in another window. Please close that window first and then try again."),
noLink: true
};
await this.windowsMainService.showMessageBox(options, this.windowsMainService.getFocusedWindow());
return false;
}
return true; // OK
}
private doOpenWorkspace(window: ICodeWindow, workspace: IWorkspaceIdentifier): IEnterWorkspaceResult {
window.focus();
// Register window for backups and migrate current backups over
let backupPath: string | undefined;
if (!window.config.extensionDevelopmentPath) {
backupPath = this.backupMainService.registerWorkspaceBackupSync({ workspace, remoteAuthority: window.remoteAuthority }, window.config.backupPath);
}
// if the window was opened on an untitled workspace, delete it.
if (window.openedWorkspace && this.workspacesMainService.isUntitledWorkspace(window.openedWorkspace)) {
this.workspacesMainService.deleteUntitledWorkspaceSync(window.openedWorkspace);
}
// Update window configuration properly based on transition to workspace
window.config.folderUri = undefined;
window.config.workspace = workspace;
window.config.backupPath = backupPath;
return { workspace, backupPath };
}
}
function resourceFromURIToOpen(openable: IWindowOpenable): URI {
if (isWorkspaceToOpen(openable)) {
return openable.workspaceUri;
}
if (isFolderToOpen(openable)) {
return openable.folderUri;
}
return openable.fileUri;
}
@@ -35,12 +35,15 @@ export function restoreWindowsState(data: WindowsStateStorageData | undefined):
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;
}
@@ -49,9 +52,11 @@ function restoreWindowState(windowState: ISerializedWindowState): IWindowState {
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) {
@@ -59,11 +64,13 @@ function restoreWindowState(windowState: ISerializedWindowState): IWindowState {
} 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;
}
+5 -9
View File
@@ -3,19 +3,18 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as os from 'os';
import * as fs from 'fs';
import { spawn, ChildProcess, SpawnOptions } from 'child_process';
import { buildHelpMessage, buildVersionMessage, addArg, createWaitMarkerFile, OPTIONS } from 'vs/platform/environment/node/argv';
import { parseCLIProcessArgv } from 'vs/platform/environment/node/argvHelper';
import { ParsedArgs } from 'vs/platform/environment/common/environment';
import product from 'vs/platform/product/common/product';
import * as paths from 'vs/base/common/path';
import * as os from 'os';
import * as fs from 'fs';
import { whenDeleted, writeFileSync } from 'vs/base/node/pfs';
import { findFreePort, randomPort } from 'vs/base/node/ports';
import { resolveTerminalEncoding } from 'vs/base/node/encoding';
import * as iconv from 'iconv-lite';
import { isWindows, isLinux } from 'vs/base/common/platform';
import { isWindows } from 'vs/base/common/platform';
import { ProfilingSession, Target } from 'v8-inspect-profiler';
import { isString } from 'vs/base/common/types';
@@ -179,7 +178,8 @@ export async function main(argv: string[]): Promise<any> {
if (!stdinFileError) {
// Pipe into tmp file using terminals encoding
resolveTerminalEncoding(verbose).then(encoding => {
resolveTerminalEncoding(verbose).then(async encoding => {
const iconv = await import('iconv-lite');
const converterStream = iconv.decodeStream(encoding);
process.stdin.pipe(converterStream).pipe(stdinFileStream);
});
@@ -360,10 +360,6 @@ export async function main(argv: string[]): Promise<any> {
options['stdio'] = 'ignore';
}
if (isLinux) {
addArg(argv, '--no-sandbox'); // Electron 6 introduces a chrome-sandbox that requires root to run. This can fail. Disable sandbox via --no-sandbox
}
const child = spawn(process.execPath, argv.slice(2), options);
if (args.wait && waitMarkerFilePath) {
-131
View File
@@ -1,131 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as platform from 'vs/base/common/platform';
import * as extpath from 'vs/base/common/extpath';
import { OpenContext } from 'vs/platform/windows/common/windows';
import { IWorkspaceIdentifier, IResolvedWorkspace, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
import { URI } from 'vs/base/common/uri';
import { isEqual, isEqualOrParent } from 'vs/base/common/resources';
export interface ISimpleWindow {
openedWorkspace?: IWorkspaceIdentifier;
openedFolderUri?: URI;
extensionDevelopmentPath?: string[];
lastFocusTime: number;
}
export interface IBestWindowOrFolderOptions<W extends ISimpleWindow> {
windows: W[];
newWindow: boolean;
context: OpenContext;
fileUri?: URI;
userHome?: string;
codeSettingsFolder?: string;
localWorkspaceResolver: (workspace: IWorkspaceIdentifier) => IResolvedWorkspace | null;
}
export function findBestWindowOrFolderForFile<W extends ISimpleWindow>({ 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 ISimpleWindow>(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 => isEqualOrParent(fileUri, folder.uri))) {
return window;
}
} else {
// use the config path instead
if (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 && 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 ISimpleWindow>(windows: W[]): W | undefined {
const lastFocusedDate = Math.max.apply(Math, windows.map(window => window.lastFocusTime));
return windows.filter(window => window.lastFocusTime === lastFocusedDate)[0];
}
export function findWindowOnWorkspace<W extends ISimpleWindow>(windows: W[], workspace: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier)): W | null {
if (isSingleFolderWorkspaceIdentifier(workspace)) {
for (const window of windows) {
// match on folder
if (isSingleFolderWorkspaceIdentifier(workspace)) {
if (window.openedFolderUri && 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 ISimpleWindow>(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 && currPaths.some(p => matches(p))) {
return window;
}
}
return null;
}
export function findWindowOnWorkspaceOrFolderUri<W extends ISimpleWindow>(windows: W[], uri: URI | undefined): W | null {
if (!uri) {
return null;
}
for (const window of windows) {
// check for workspace config path
if (window.openedWorkspace && isEqual(window.openedWorkspace.configPath, uri)) {
return window;
}
// check for folder path
if (window.openedFolderUri && isEqual(window.openedFolderUri, uri)) {
return window;
}
}
return null;
}
@@ -11,7 +11,7 @@ import * as platform from 'vs/base/common/platform';
import { CharWidthRequest, CharWidthRequestType, readCharWidths } from 'vs/editor/browser/config/charWidthReader';
import { ElementSizeObserver } from 'vs/editor/browser/config/elementSizeObserver';
import { CommonEditorConfiguration, IEnvConfiguration } from 'vs/editor/common/config/commonEditorConfig';
import { IEditorOptions, EditorOption } from 'vs/editor/common/config/editorOptions';
import { EditorOption, IEditorConstructionOptions } from 'vs/editor/common/config/editorOptions';
import { BareFontInfo, FontInfo } from 'vs/editor/common/config/fontInfo';
import { IDimension } from 'vs/editor/common/editorCommon';
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
@@ -310,13 +310,13 @@ export class Configuration extends CommonEditorConfiguration {
constructor(
isSimpleWidget: boolean,
options: IEditorOptions,
options: IEditorConstructionOptions,
referenceDomElement: HTMLElement | null = null,
private readonly accessibilityService: IAccessibilityService
) {
super(isSimpleWidget, options);
this._elementSizeObserver = this._register(new ElementSizeObserver(referenceDomElement, () => this._onReferenceDomElementSizeChanged()));
this._elementSizeObserver = this._register(new ElementSizeObserver(referenceDomElement, options.dimension, () => this._onReferenceDomElementSizeChanged()));
this._register(CSSBasedConfiguration.INSTANCE.onDidChange(() => this._onCSSBasedConfigurationChanged()));
@@ -14,14 +14,14 @@ export class ElementSizeObserver extends Disposable {
private width: number;
private height: number;
constructor(referenceDomElement: HTMLElement | null, changeCallback: () => void) {
constructor(referenceDomElement: HTMLElement | null, dimension: IDimension | undefined, changeCallback: () => void) {
super();
this.referenceDomElement = referenceDomElement;
this.changeCallback = changeCallback;
this.measureReferenceDomElementToken = -1;
this.width = -1;
this.height = -1;
this.measureReferenceDomElement(false);
this.measureReferenceDomElement(false, dimension);
}
public dispose(): void {
@@ -75,4 +75,4 @@ export class ElementSizeObserver extends Disposable {
}
}
}
}
@@ -190,7 +190,7 @@ class TouchHandler extends MouseHandler {
constructor(context: ViewContext, viewController: ViewController, viewHelper: IPointerHandlerHelper) {
super(context, viewController, viewHelper);
Gesture.addTarget(this.viewHelper.linesContentDomNode);
this._register(Gesture.addTarget(this.viewHelper.linesContentDomNode));
this._register(dom.addDisposableListener(this.viewHelper.linesContentDomNode, EventType.Tap, (e) => this.onTap(e)));
this._register(dom.addDisposableListener(this.viewHelper.linesContentDomNode, EventType.Change, (e) => this.onChange(e)));
@@ -152,6 +152,10 @@ export class TextAreaHandler extends ViewPart {
this.textArea.setAttribute('aria-haspopup', 'false');
this.textArea.setAttribute('aria-autocomplete', 'both');
if (platform.isWeb && options.get(EditorOption.readOnly)) {
this.textArea.setAttribute('readonly', 'true');
}
this.textAreaCover = createFastDomNode(document.createElement('div'));
this.textAreaCover.setPosition('absolute');
@@ -395,6 +399,14 @@ export class TextAreaHandler extends ViewPart {
this._copyWithSyntaxHighlighting = options.get(EditorOption.copyWithSyntaxHighlighting);
this.textArea.setAttribute('aria-label', this._getAriaLabel(options));
if (platform.isWeb && e.hasChanged(EditorOption.readOnly)) {
if (options.get(EditorOption.readOnly)) {
this.textArea.setAttribute('readonly', 'true');
} else {
this.textArea.removeAttribute('readonly');
}
}
if (e.hasChanged(EditorOption.accessibilitySupport)) {
this._textAreaInput.writeScreenReaderContent('strategy changed');
}
@@ -29,6 +29,7 @@ import { registerThemingParticipant } from 'vs/platform/theme/common/themeServic
import { ModelDecorationMinimapOptions } from 'vs/editor/common/model/textModel';
import { Selection } from 'vs/editor/common/core/selection';
import { Color } from 'vs/base/common/color';
import { GestureEvent, EventType, Gesture } from 'vs/base/browser/touch';
function getMinimapLineHeight(renderMinimap: RenderMinimap): number {
if (renderMinimap === RenderMinimap.Large) {
@@ -208,6 +209,10 @@ class MinimapLayout {
return Math.round(desiredSliderPosition / this._computedSliderRatio);
}
public getDesiredScrollTopFromTouchLocation(pageY: number): number {
return Math.round((pageY - this.sliderHeight / 2) / this._computedSliderRatio);
}
public static create(
options: MinimapOptions,
viewportStartLineNumber: number,
@@ -451,12 +456,17 @@ export class Minimap extends ViewPart {
private readonly _mouseDownListener: IDisposable;
private readonly _sliderMouseMoveMonitor: GlobalMouseMoveMonitor<IStandardMouseMoveEventData>;
private readonly _sliderMouseDownListener: IDisposable;
private readonly _gestureDisposable: IDisposable;
private readonly _sliderTouchStartListener: IDisposable;
private readonly _sliderTouchMoveListener: IDisposable;
private readonly _sliderTouchEndListener: IDisposable;
private _options: MinimapOptions;
private _lastRenderData: RenderData | null;
private _selections: Selection[] = [];
private _selectionColor: Color | undefined;
private _renderDecorations: boolean = false;
private _gestureInProgress: boolean = false;
private _buffers: MinimapBuffers | null;
constructor(context: ViewContext) {
@@ -566,12 +576,50 @@ export class Minimap extends ViewPart {
);
}
});
this._gestureDisposable = Gesture.addTarget(this._domNode.domNode);
this._sliderTouchStartListener = dom.addDisposableListener(this._domNode.domNode, EventType.Start, (e: GestureEvent) => {
e.preventDefault();
e.stopPropagation();
if (this._lastRenderData) {
this._slider.toggleClassName('active', true);
this._gestureInProgress = true;
this.scrollDueToTouchEvent(e);
}
});
this._sliderTouchMoveListener = dom.addStandardDisposableListener(this._domNode.domNode, EventType.Change, (e: GestureEvent) => {
e.preventDefault();
e.stopPropagation();
if (this._lastRenderData && this._gestureInProgress) {
this.scrollDueToTouchEvent(e);
}
});
this._sliderTouchEndListener = dom.addStandardDisposableListener(this._domNode.domNode, EventType.End, (e: GestureEvent) => {
e.preventDefault();
e.stopPropagation();
this._gestureInProgress = false;
this._slider.toggleClassName('active', false);
});
}
private scrollDueToTouchEvent(touch: GestureEvent) {
const startY = this._domNode.domNode.getBoundingClientRect().top;
const scrollTop = this._lastRenderData!.renderedLayout.getDesiredScrollTopFromTouchLocation(touch.pageY - startY);
this._context.viewLayout.setScrollPositionNow({
scrollTop: scrollTop
});
}
public dispose(): void {
this._mouseDownListener.dispose();
this._sliderMouseMoveMonitor.dispose();
this._sliderMouseDownListener.dispose();
this._gestureDisposable.dispose();
this._sliderTouchStartListener.dispose();
this._sliderTouchMoveListener.dispose();
this._sliderTouchEndListener.dispose();
super.dispose();
}
@@ -23,7 +23,7 @@ import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService
import { ICommandDelegate } from 'vs/editor/browser/view/viewController';
import { IContentWidgetData, IOverlayWidgetData, View } from 'vs/editor/browser/view/viewImpl';
import { ViewOutgoingEvents } from 'vs/editor/browser/view/viewOutgoingEvents';
import { ConfigurationChangedEvent, EditorLayoutInfo, IEditorOptions, EditorOption, IComputedEditorOptions, FindComputedEditorOptionValueById } from 'vs/editor/common/config/editorOptions';
import { ConfigurationChangedEvent, EditorLayoutInfo, IEditorOptions, EditorOption, IComputedEditorOptions, FindComputedEditorOptionValueById, IEditorConstructionOptions } from 'vs/editor/common/config/editorOptions';
import { Cursor, CursorStateChangedEvent } from 'vs/editor/common/controller/cursor';
import { CursorColumns, ICursors } from 'vs/editor/common/controller/cursorCommon';
import { ICursorPositionChangedEvent, ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents';
@@ -236,7 +236,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
constructor(
domElement: HTMLElement,
options: IEditorOptions,
options: IEditorConstructionOptions,
codeEditorWidgetOptions: ICodeEditorWidgetOptions,
@IInstantiationService instantiationService: IInstantiationService,
@ICodeEditorService codeEditorService: ICodeEditorService,
@@ -329,7 +329,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
this._codeEditorService.addCodeEditor(this);
}
protected _createConfiguration(options: IEditorOptions, accessibilityService: IAccessibilityService): editorCommon.IConfiguration {
protected _createConfiguration(options: IEditorConstructionOptions, accessibilityService: IAccessibilityService): editorCommon.IConfiguration {
return new Configuration(this.isSimpleWidget, options, this._domElement, accessibilityService);
}
@@ -12,6 +12,7 @@ import { USUAL_WORD_SEPARATORS } from 'vs/editor/common/model/wordHelper';
import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility';
import { isObject } from 'vs/base/common/types';
import { IConfigurationPropertySchema } from 'vs/platform/configuration/common/configurationRegistry';
import { IDimension } from 'vs/editor/common/editorCommon';
//#region typed options
@@ -516,6 +517,13 @@ export interface IEditorOptions {
showUnused?: boolean;
}
export interface IEditorConstructionOptions extends IEditorOptions {
/**
* The initial editor dimension (to avoid measuring the container).
*/
dimension?: IDimension;
}
/**
* Configuration options for the diff editor.
*/
+2
View File
@@ -280,6 +280,8 @@ export interface IEditor {
/**
* Instructs the editor to remeasure its container. This method should
* be called when the container of the editor gets resized.
*
* If a dimension is passed in, the passed in value will be used.
*/
layout(dimension?: IDimension): void;
+1 -1
View File
@@ -922,7 +922,7 @@ export interface DocumentSymbol {
/**
* The document symbol provider interface defines the contract between extensions and
* the [go to symbol](https://code.visualstudio.com/docs/editor/editingevolved#_goto-symbol)-feature.
* the [go to symbol](https://code.visualstudio.com/docs/editor/editingevolved#_go-to-symbol)-feature.
*/
export interface DocumentSymbolProvider {
+26 -1
View File
@@ -14,7 +14,7 @@ import { ScrollType, IEditorContribution } from 'vs/editor/common/editorCommon';
import { ITextModel } from 'vs/editor/common/model';
import { registerEditorAction, registerEditorContribution, ServicesAccessor, EditorAction, registerInstantiatedEditorAction } from 'vs/editor/browser/editorExtensions';
import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser';
import { FoldingModel, setCollapseStateAtLevel, CollapseMemento, setCollapseStateLevelsDown, setCollapseStateLevelsUp, setCollapseStateForMatchingLines, setCollapseStateForType } from 'vs/editor/contrib/folding/foldingModel';
import { FoldingModel, setCollapseStateAtLevel, CollapseMemento, setCollapseStateLevelsDown, setCollapseStateLevelsUp, setCollapseStateForMatchingLines, setCollapseStateForType, toggleCollapseState } from 'vs/editor/contrib/folding/foldingModel';
import { FoldingDecorationProvider } from './foldingDecorations';
import { FoldingRegions, FoldingRegion } from './foldingRanges';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
@@ -656,6 +656,30 @@ class FoldAction extends FoldingAction<FoldingArguments> {
}
}
class ToggleFoldAction extends FoldingAction<void> {
constructor() {
super({
id: 'editor.toggleFold',
label: nls.localize('toggleFoldAction.label', "Toggle Fold"),
alias: 'Toggle Fold',
precondition: CONTEXT_FOLDING_ENABLED,
kbOpts: {
kbExpr: EditorContextKeys.editorTextFocus,
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_K),
weight: KeybindingWeight.EditorContrib
}
});
}
invoke(_foldingController: FoldingController, foldingModel: FoldingModel, editor: ICodeEditor): void {
let selectedLines = this.getSelectedLines(editor);
toggleCollapseState(foldingModel, 1, selectedLines);
}
}
class FoldRecursivelyAction extends FoldingAction<void> {
constructor() {
@@ -842,6 +866,7 @@ registerEditorAction(UnfoldAllAction);
registerEditorAction(FoldAllBlockCommentsAction);
registerEditorAction(FoldAllRegionsAction);
registerEditorAction(UnfoldAllRegionsAction);
registerEditorAction(ToggleFoldAction);
for (let i = 1; i <= 7; i++) {
registerInstantiatedEditorAction(
@@ -242,6 +242,26 @@ export class FoldingModel {
}
/**
* Collapse or expand the regions at the given locations
* @param levels The number of levels. Use 1 to only impact the regions at the location, use Number.MAX_VALUE for all levels.
* @param lineNumbers the location of the regions to collapse or expand, or if not set, all regions in the model.
*/
export function toggleCollapseState(foldingModel: FoldingModel, levels: number, lineNumbers: number[]) {
let toToggle: FoldingRegion[] = [];
for (let lineNumber of lineNumbers) {
let region = foldingModel.getRegionAtLine(lineNumber);
if (region) {
const doCollapse = !region.isCollapsed;
toToggle.push(region);
if (levels > 1) {
let regionsInside = foldingModel.getRegionsInside(region, (r, level: number) => r.isCollapsed !== doCollapse && level < levels);
toToggle.push(...regionsInside);
}
}
}
foldingModel.toggleCollapseState(toToggle);
}
/**
+1 -1
View File
@@ -37,7 +37,7 @@
margin: 8px 0;
}
.monaco-editor-hover p > code {
.monaco-editor-hover code {
font-family: var(--monaco-monospace-font);
}
@@ -53,7 +53,7 @@
padding-left: 20px;
}
.monaco-editor .suggest-widget > .details p > code {
.monaco-editor .suggest-widget > .details p code {
font-family: var(--monaco-monospace-font);
}
@@ -21,7 +21,7 @@ import { Selection } from 'vs/editor/common/core/selection';
import { IEditorContribution } from 'vs/editor/common/editorCommon';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { ToggleTabFocusModeAction } from 'vs/editor/contrib/toggleTabFocusMode/toggleTabFocusMode';
import { IEditorConstructionOptions } from 'vs/editor/standalone/browser/standaloneCodeEditor';
import { IStandaloneEditorConstructionOptions } from 'vs/editor/standalone/browser/standaloneCodeEditor';
import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
@@ -164,7 +164,7 @@ class AccessibilityHelpWidget extends Widget implements IOverlayWidget {
if (e.equals(KeyMod.CtrlCmd | KeyCode.KEY_H)) {
alert(AccessibilityHelpNLS.openingDocs);
let url = (<IEditorConstructionOptions>this._editor.getRawOptions()).accessibilityHelpUrl;
let url = (<IStandaloneEditorConstructionOptions>this._editor.getRawOptions()).accessibilityHelpUrl;
if (typeof url === 'undefined') {
url = 'https://go.microsoft.com/fwlink/?linkid=852450';
}
@@ -10,7 +10,7 @@ import { ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget';
import { IDiffEditorOptions, IEditorOptions } from 'vs/editor/common/config/editorOptions';
import { IDiffEditorOptions, IEditorOptions, IEditorConstructionOptions } from 'vs/editor/common/config/editorOptions';
import { InternalEditorAction } from 'vs/editor/common/editorAction';
import { IModelChangedEvent } from 'vs/editor/common/editorCommon';
import { ITextModel } from 'vs/editor/common/model';
@@ -79,7 +79,7 @@ export interface IActionDescriptor {
/**
* The options to create an editor.
*/
export interface IEditorConstructionOptions extends IEditorOptions {
export interface IStandaloneEditorConstructionOptions extends IEditorConstructionOptions {
/**
* The initial model associated with this code editor.
*/
@@ -158,7 +158,7 @@ export class StandaloneCodeEditor extends CodeEditorWidget implements IStandalon
constructor(
domElement: HTMLElement,
options: IEditorConstructionOptions,
options: IStandaloneEditorConstructionOptions,
@IInstantiationService instantiationService: IInstantiationService,
@ICodeEditorService codeEditorService: ICodeEditorService,
@ICommandService commandService: ICommandService,
@@ -287,7 +287,7 @@ export class StandaloneEditor extends StandaloneCodeEditor implements IStandalon
constructor(
domElement: HTMLElement,
options: IEditorConstructionOptions | undefined,
options: IStandaloneEditorConstructionOptions | undefined,
toDispose: IDisposable,
@IInstantiationService instantiationService: IInstantiationService,
@ICodeEditorService codeEditorService: ICodeEditorService,
@@ -24,7 +24,7 @@ import { IWebWorkerOptions, MonacoWebWorker, createWebWorker as actualCreateWebW
import * as standaloneEnums from 'vs/editor/common/standalone/standaloneEnums';
import { Colorizer, IColorizerElementOptions, IColorizerOptions } from 'vs/editor/standalone/browser/colorizer';
import { SimpleEditorModelResolverService } from 'vs/editor/standalone/browser/simpleServices';
import { IDiffEditorConstructionOptions, IEditorConstructionOptions, IStandaloneCodeEditor, IStandaloneDiffEditor, StandaloneDiffEditor, StandaloneEditor } from 'vs/editor/standalone/browser/standaloneCodeEditor';
import { IDiffEditorConstructionOptions, IStandaloneEditorConstructionOptions, IStandaloneCodeEditor, IStandaloneDiffEditor, StandaloneDiffEditor, StandaloneEditor } from 'vs/editor/standalone/browser/standaloneCodeEditor';
import { DynamicStandaloneServices, IEditorOverrideServices, StaticServices } from 'vs/editor/standalone/browser/standaloneServices';
import { IStandaloneThemeData, IStandaloneThemeService } from 'vs/editor/standalone/common/standaloneThemeService';
import { ICommandService } from 'vs/platform/commands/common/commands';
@@ -68,7 +68,7 @@ function withAllStandaloneServices<T extends editorCommon.IEditor>(domElement: H
* `domElement` should be empty (not contain other dom nodes).
* The editor will read the size of `domElement`.
*/
export function create(domElement: HTMLElement, options?: IEditorConstructionOptions, override?: IEditorOverrideServices): IStandaloneCodeEditor {
export function create(domElement: HTMLElement, options?: IStandaloneEditorConstructionOptions, override?: IEditorOverrideServices): IStandaloneCodeEditor {
return withAllStandaloneServices(domElement, override || {}, (services) => {
return new StandaloneEditor(
domElement,
+1 -1
View File
@@ -29,7 +29,7 @@ import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService
export class TestCodeEditor extends CodeEditorWidget implements editorBrowser.ICodeEditor {
//#region testing overrides
protected _createConfiguration(options: editorOptions.IEditorOptions): editorCommon.IConfiguration {
protected _createConfiguration(options: editorOptions.IEditorConstructionOptions): editorCommon.IConfiguration {
return new TestConfiguration(options);
}
protected _createView(viewModel: ViewModel, cursor: Cursor): [View, boolean] {
+12 -3
View File
@@ -813,7 +813,7 @@ declare namespace monaco.editor {
* `domElement` should be empty (not contain other dom nodes).
* The editor will read the size of `domElement`.
*/
export function create(domElement: HTMLElement, options?: IEditorConstructionOptions, override?: IEditorOverrideServices): IStandaloneCodeEditor;
export function create(domElement: HTMLElement, options?: IStandaloneEditorConstructionOptions, override?: IEditorOverrideServices): IStandaloneCodeEditor;
/**
* Emitted when an editor is created.
@@ -1051,7 +1051,7 @@ declare namespace monaco.editor {
/**
* The options to create an editor.
*/
export interface IEditorConstructionOptions extends IEditorOptions {
export interface IStandaloneEditorConstructionOptions extends IEditorConstructionOptions {
/**
* The initial model associated with this code editor.
*/
@@ -2061,6 +2061,8 @@ declare namespace monaco.editor {
/**
* Instructs the editor to remeasure its container. This method should
* be called when the container of the editor gets resized.
*
* If a dimension is passed in, the passed in value will be used.
*/
layout(dimension?: IDimension): void;
/**
@@ -2918,6 +2920,13 @@ declare namespace monaco.editor {
showUnused?: boolean;
}
export interface IEditorConstructionOptions extends IEditorOptions {
/**
* The initial editor dimension (to avoid measuring the container).
*/
dimension?: IDimension;
}
/**
* Configuration options for the diff editor.
*/
@@ -5131,7 +5140,7 @@ declare namespace monaco.languages {
/**
* The document symbol provider interface defines the contract between extensions and
* the [go to symbol](https://code.visualstudio.com/docs/editor/editingevolved#_goto-symbol)-feature.
* the [go to symbol](https://code.visualstudio.com/docs/editor/editingevolved#_go-to-symbol)-feature.
*/
export interface DocumentSymbolProvider {
displayName?: string;
@@ -8,6 +8,7 @@ import { IAuthTokenService, AuthTokenStatus } from 'vs/platform/auth/common/auth
import { ICredentialsService } from 'vs/platform/credentials/common/credentials';
import { Disposable } from 'vs/base/common/lifecycle';
import { IProductService } from 'vs/platform/product/common/productService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
const SERVICE_NAME = 'VS Code';
const ACCOUNT = 'MyAccount';
@@ -23,9 +24,10 @@ export class AuthTokenService extends Disposable implements IAuthTokenService {
constructor(
@ICredentialsService private readonly credentialsService: ICredentialsService,
@IProductService productService: IProductService,
@IConfigurationService configurationService: IConfigurationService,
) {
super();
if (productService.settingsSyncStoreUrl) {
if (productService.settingsSyncStoreUrl && configurationService.getValue('configurationSync.enableAuth')) {
this._status = AuthTokenStatus.Inactive;
this.getToken().then(token => {
if (token) {
@@ -0,0 +1,211 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { MessageBoxOptions, SaveDialogOptions, OpenDialogOptions, dialog, FileFilter, BrowserWindow } from 'electron';
import { Queue } from 'vs/base/common/async';
import { IStateService } from 'vs/platform/state/node/state';
import { isMacintosh } from 'vs/base/common/platform';
import { dirname } from 'vs/base/common/path';
import { normalizeNFC } from 'vs/base/common/normalization';
import { exists } from 'vs/base/node/pfs';
import { INativeOpenDialogOptions, MessageBoxReturnValue, SaveDialogReturnValue, OpenDialogReturnValue } from 'vs/platform/dialogs/node/dialogs';
import { withNullAsUndefined } from 'vs/base/common/types';
import { localize } from 'vs/nls';
import { WORKSPACE_FILTER } from 'vs/platform/workspaces/common/workspaces';
import { mnemonicButtonLabel } from 'vs/base/common/labels';
export const IDialogMainService = createDecorator<IDialogMainService>('dialogMainService');
export interface IDialogMainService {
_serviceBrand: undefined;
pickFileFolder(options: INativeOpenDialogOptions, window?: BrowserWindow): Promise<string[] | undefined>;
pickFolder(options: INativeOpenDialogOptions, window?: BrowserWindow): Promise<string[] | undefined>;
pickFile(options: INativeOpenDialogOptions, window?: BrowserWindow): Promise<string[] | undefined>;
pickWorkspace(options: INativeOpenDialogOptions, window?: BrowserWindow): Promise<string[] | undefined>;
showMessageBox(options: MessageBoxOptions, window?: BrowserWindow): Promise<MessageBoxReturnValue>;
showSaveDialog(options: SaveDialogOptions, window?: BrowserWindow): Promise<SaveDialogReturnValue>;
showOpenDialog(options: OpenDialogOptions, window?: BrowserWindow): Promise<OpenDialogReturnValue>;
}
interface IInternalNativeOpenDialogOptions extends INativeOpenDialogOptions {
pickFolders?: boolean;
pickFiles?: boolean;
title: string;
buttonLabel?: string;
filters?: FileFilter[];
}
export class DialogMainService implements IDialogMainService {
_serviceBrand: undefined;
private static readonly workingDirPickerStorageKey = 'pickerWorkingDir';
private readonly mapWindowToDialogQueue: Map<number, Queue<void>>;
private readonly noWindowDialogQueue: Queue<void>;
constructor(
@IStateService private readonly stateService: IStateService
) {
this.mapWindowToDialogQueue = new Map<number, Queue<void>>();
this.noWindowDialogQueue = new Queue<void>();
}
pickFileFolder(options: INativeOpenDialogOptions, window?: BrowserWindow): Promise<string[] | undefined> {
return this.doPick({ ...options, pickFolders: true, pickFiles: true, title: localize('open', "Open") }, window);
}
pickFolder(options: INativeOpenDialogOptions, window?: BrowserWindow): Promise<string[] | undefined> {
return this.doPick({ ...options, pickFolders: true, title: localize('openFolder', "Open Folder") }, window);
}
pickFile(options: INativeOpenDialogOptions, window?: BrowserWindow): Promise<string[] | undefined> {
return this.doPick({ ...options, pickFiles: true, title: localize('openFile', "Open File") }, window);
}
pickWorkspace(options: INativeOpenDialogOptions, window?: BrowserWindow): Promise<string[] | undefined> {
const title = localize('openWorkspaceTitle', "Open Workspace");
const buttonLabel = mnemonicButtonLabel(localize({ key: 'openWorkspace', comment: ['&& denotes a mnemonic'] }, "&&Open"));
const filters = WORKSPACE_FILTER;
return this.doPick({ ...options, pickFiles: true, title, filters, buttonLabel }, window);
}
private async doPick(options: IInternalNativeOpenDialogOptions, window?: BrowserWindow): Promise<string[] | undefined> {
// Ensure dialog options
const dialogOptions: OpenDialogOptions = {
title: options.title,
buttonLabel: options.buttonLabel,
filters: options.filters
};
// Ensure defaultPath
dialogOptions.defaultPath = options.defaultPath || this.stateService.getItem<string>(DialogMainService.workingDirPickerStorageKey);
// Ensure properties
if (typeof options.pickFiles === 'boolean' || typeof options.pickFolders === 'boolean') {
dialogOptions.properties = undefined; // let it override based on the booleans
if (options.pickFiles && options.pickFolders) {
dialogOptions.properties = ['multiSelections', 'openDirectory', 'openFile', 'createDirectory'];
}
}
if (!dialogOptions.properties) {
dialogOptions.properties = ['multiSelections', options.pickFolders ? 'openDirectory' : 'openFile', 'createDirectory'];
}
if (isMacintosh) {
dialogOptions.properties.push('treatPackageAsDirectory'); // always drill into .app files
}
// Show Dialog
const windowToUse = window || BrowserWindow.getFocusedWindow();
const result = await this.showOpenDialog(dialogOptions, withNullAsUndefined(windowToUse));
if (result && result.filePaths && result.filePaths.length > 0) {
// Remember path in storage for next time
this.stateService.setItem(DialogMainService.workingDirPickerStorageKey, dirname(result.filePaths[0]));
return result.filePaths;
}
return undefined; // {{SQL CARBON EDIT}} strict-null-check
}
private getDialogQueue(window?: BrowserWindow): Queue<any> {
if (!window) {
return this.noWindowDialogQueue;
}
let windowDialogQueue = this.mapWindowToDialogQueue.get(window.id);
if (!windowDialogQueue) {
windowDialogQueue = new Queue<any>();
this.mapWindowToDialogQueue.set(window.id, windowDialogQueue);
}
return windowDialogQueue;
}
showMessageBox(options: MessageBoxOptions, window?: BrowserWindow): Promise<MessageBoxReturnValue> {
return this.getDialogQueue(window).queue(async () => {
return new Promise(resolve => {
if (window) {
return dialog.showMessageBox(window, options, (response, checkboxChecked) => resolve({ response, checkboxChecked }));
}
return dialog.showMessageBox(options);
});
});
}
showSaveDialog(options: SaveDialogOptions, window?: BrowserWindow): Promise<SaveDialogReturnValue> {
function normalizePath(path: string | undefined): string | undefined {
if (path && isMacintosh) {
path = normalizeNFC(path); // normalize paths returned from the OS
}
return path;
}
return this.getDialogQueue(window).queue(async () => {
return new Promise<SaveDialogReturnValue>(resolve => {
if (window) {
dialog.showSaveDialog(window, options, filePath => resolve({ filePath }));
} else {
dialog.showSaveDialog(options, filePath => resolve({ filePath }));
}
}).then(result => {
result.filePath = normalizePath(result.filePath);
return result;
});
});
}
showOpenDialog(options: OpenDialogOptions, window?: BrowserWindow): Promise<OpenDialogReturnValue> {
function normalizePaths(paths: string[] | undefined): string[] | undefined {
if (paths && paths.length > 0 && isMacintosh) {
paths = paths.map(path => normalizeNFC(path)); // normalize paths returned from the OS
}
return paths;
}
return this.getDialogQueue(window).queue(async () => {
// Ensure the path exists (if provided)
if (options.defaultPath) {
const pathExists = await exists(options.defaultPath);
if (!pathExists) {
options.defaultPath = undefined;
}
}
// Show dialog
return new Promise<OpenDialogReturnValue>(resolve => {
if (window) {
dialog.showOpenDialog(window, options, filePaths => resolve({ filePaths }));
} else {
dialog.showOpenDialog(options, filePaths => resolve({ filePaths }));
}
}).then(result => {
result.filePaths = normalizePaths(result.filePaths);
return result;
});
});
}
}
+13
View File
@@ -13,3 +13,16 @@ export interface INativeOpenDialogOptions {
telemetryEventName?: string;
telemetryExtraData?: ITelemetryData;
}
export interface MessageBoxReturnValue {
response: number;
checkboxChecked: boolean;
}
export interface SaveDialogReturnValue {
filePath?: string;
}
export interface OpenDialogReturnValue {
filePaths?: string[];
}
@@ -5,16 +5,17 @@
import { Event } from 'vs/base/common/event';
import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows';
import { MessageBoxOptions, MessageBoxReturnValue, shell, OpenDevToolsOptions, SaveDialogOptions, SaveDialogReturnValue, OpenDialogOptions, OpenDialogReturnValue, CrashReporterStartOptions, crashReporter, Menu, BrowserWindow, app } from 'electron';
import { INativeOpenInWindowOptions } from 'vs/platform/windows/node/window';
import { MessageBoxOptions, shell, OpenDevToolsOptions, SaveDialogOptions, OpenDialogOptions, CrashReporterStartOptions, crashReporter, Menu, BrowserWindow, app } from 'electron';
import { INativeOpenWindowOptions } from 'vs/platform/windows/node/window';
import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
import { IOpenedWindow, OpenContext, IWindowOpenable, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows';
import { INativeOpenDialogOptions } from 'vs/platform/dialogs/node/dialogs';
import { INativeOpenDialogOptions, MessageBoxReturnValue, SaveDialogReturnValue, OpenDialogReturnValue } from 'vs/platform/dialogs/node/dialogs';
import { isMacintosh, IProcessEnvironment } from 'vs/base/common/platform';
import { IElectronService } from 'vs/platform/electron/node/electron';
import { ISerializableCommandAction } from 'vs/platform/actions/common/actions';
import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment';
import { AddFirstParameterToFunctions } from 'vs/base/common/types';
import { IDialogMainService } from 'vs/platform/dialogs/electron-main/dialogs';
export class ElectronMainService implements AddFirstParameterToFunctions<IElectronService, Promise<any> /* only methods, not events */, number /* window ID */> {
@@ -22,6 +23,7 @@ export class ElectronMainService implements AddFirstParameterToFunctions<IElectr
constructor(
@IWindowsMainService private readonly windowsMainService: IWindowsMainService,
@IDialogMainService private readonly dialogMainService: IDialogMainService,
@ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService,
@IEnvironmentService private readonly environmentService: IEnvironmentService
) {
@@ -61,7 +63,7 @@ export class ElectronMainService implements AddFirstParameterToFunctions<IElectr
}
async getActiveWindowId(windowId: number): Promise<number | undefined> {
const activeWindow = this.windowsMainService.getFocusedWindow() || this.windowsMainService.getLastActiveWindow();
const activeWindow = BrowserWindow.getFocusedWindow() || this.windowsMainService.getLastActiveWindow();
if (activeWindow) {
return activeWindow.id;
}
@@ -69,11 +71,17 @@ export class ElectronMainService implements AddFirstParameterToFunctions<IElectr
return undefined;
}
async openEmptyWindow(windowId: number, options?: IOpenEmptyWindowOptions): Promise<void> {
this.windowsMainService.openEmptyWindow(OpenContext.API, options);
openWindow(windowId: number, options?: IOpenEmptyWindowOptions): Promise<void>;
openWindow(windowId: number, toOpen: IWindowOpenable[], options?: INativeOpenWindowOptions): Promise<void>;
openWindow(windowId: number, arg1?: IOpenEmptyWindowOptions | IWindowOpenable[], arg2?: INativeOpenWindowOptions): Promise<void> {
if (Array.isArray(arg1)) {
return this.doOpenWindow(windowId, arg1, arg2);
}
return this.doOpenEmptyWindow(windowId, arg1);
}
async openInWindow(windowId: number, toOpen: IWindowOpenable[], options: INativeOpenInWindowOptions = Object.create(null)): Promise<void> {
private async doOpenWindow(windowId: number, toOpen: IWindowOpenable[], options: INativeOpenWindowOptions = Object.create(null)): Promise<void> {
if (toOpen.length > 0) {
this.windowsMainService.open({
context: OpenContext.API,
@@ -91,6 +99,10 @@ export class ElectronMainService implements AddFirstParameterToFunctions<IElectr
}
}
private async doOpenEmptyWindow(windowId: number, options?: IOpenEmptyWindowOptions): Promise<void> {
this.windowsMainService.openEmptyWindow(OpenContext.API, options);
}
async toggleFullScreen(windowId: number): Promise<void> {
const window = this.windowsMainService.getWindowById(windowId);
if (window) {
@@ -164,15 +176,24 @@ export class ElectronMainService implements AddFirstParameterToFunctions<IElectr
//#region Dialog
async showMessageBox(windowId: number, options: MessageBoxOptions): Promise<MessageBoxReturnValue> {
return this.windowsMainService.showMessageBox(options, this.windowsMainService.getWindowById(windowId));
return this.dialogMainService.showMessageBox(options, this.toBrowserWindow(windowId));
}
async showSaveDialog(windowId: number, options: SaveDialogOptions): Promise<SaveDialogReturnValue> {
return this.windowsMainService.showSaveDialog(options, this.windowsMainService.getWindowById(windowId));
return this.dialogMainService.showSaveDialog(options, this.toBrowserWindow(windowId));
}
async showOpenDialog(windowId: number, options: OpenDialogOptions): Promise<OpenDialogReturnValue> {
return this.windowsMainService.showOpenDialog(options, this.windowsMainService.getWindowById(windowId));
return this.dialogMainService.showOpenDialog(options, this.toBrowserWindow(windowId));
}
private toBrowserWindow(windowId: number): BrowserWindow | undefined {
const window = this.windowsMainService.getWindowById(windowId);
if (window) {
return window.win;
}
return undefined;
}
async pickFileFolderAndOpen(windowId: number, options: INativeOpenDialogOptions): Promise<void> {
@@ -214,7 +235,9 @@ export class ElectronMainService implements AddFirstParameterToFunctions<IElectr
}
async openExternal(windowId: number, url: string): Promise<boolean> {
return this.windowsMainService.openExternal(url);
shell.openExternal(url);
return true;
}
async updateTouchBar(windowId: number, items: ISerializableCommandAction[][]): Promise<void> {
@@ -229,7 +252,7 @@ export class ElectronMainService implements AddFirstParameterToFunctions<IElectr
//#region macOS Touchbar
async newWindowTab(): Promise<void> {
this.windowsMainService.openNewTabbedWindow(OpenContext.API);
this.windowsMainService.open({ context: OpenContext.API, cli: this.environmentService.args, forceNewTabbedWindow: true, forceEmpty: true });
}
async showPreviousWindowTab(): Promise<void> {
@@ -267,7 +290,7 @@ export class ElectronMainService implements AddFirstParameterToFunctions<IElectr
}
}
async closeWorkpsace(windowId: number): Promise<void> {
async closeWorkspace(windowId: number): Promise<void> {
const window = this.windowsMainService.getWindowById(windowId);
if (window) {
return this.windowsMainService.closeWorkspace(window);
+6 -6
View File
@@ -4,14 +4,14 @@
*--------------------------------------------------------------------------------------------*/
import { Event } from 'vs/base/common/event';
import { MessageBoxOptions, MessageBoxReturnValue, OpenDevToolsOptions, SaveDialogOptions, OpenDialogOptions, OpenDialogReturnValue, SaveDialogReturnValue, CrashReporterStartOptions } from 'electron';
import { MessageBoxOptions, OpenDevToolsOptions, SaveDialogOptions, OpenDialogOptions, CrashReporterStartOptions } from 'electron';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IWindowOpenable, IOpenEmptyWindowOptions, IOpenedWindow } from 'vs/platform/windows/common/windows';
import { INativeOpenDialogOptions } from 'vs/platform/dialogs/node/dialogs';
import { INativeOpenDialogOptions, MessageBoxReturnValue, SaveDialogReturnValue, OpenDialogReturnValue } from 'vs/platform/dialogs/node/dialogs';
import { ISerializableCommandAction } from 'vs/platform/actions/common/actions';
import { ParsedArgs } from 'vscode-minimist';
import { IProcessEnvironment } from 'vs/base/common/platform';
import { INativeOpenInWindowOptions } from 'vs/platform/windows/node/window';
import { INativeOpenWindowOptions } from 'vs/platform/windows/node/window';
export const IElectronService = createDecorator<IElectronService>('electronService');
@@ -33,8 +33,8 @@ export interface IElectronService {
getWindowCount(): Promise<number>;
getActiveWindowId(): Promise<number | undefined>;
openEmptyWindow(options?: IOpenEmptyWindowOptions): Promise<void>;
openInWindow(toOpen: IWindowOpenable[], options?: INativeOpenInWindowOptions): Promise<void>;
openWindow(options?: IOpenEmptyWindowOptions): Promise<void>;
openWindow(toOpen: IWindowOpenable[], options?: INativeOpenWindowOptions): Promise<void>;
toggleFullScreen(): Promise<void>;
@@ -76,7 +76,7 @@ export interface IElectronService {
// Lifecycle
relaunch(options?: { addArgs?: string[], removeArgs?: string[] }): Promise<void>;
reload(): Promise<void>;
closeWorkpsace(): Promise<void>;
closeWorkspace(): Promise<void>;
closeWindow(): Promise<void>;
quit(): Promise<void>;
@@ -4,7 +4,6 @@
*--------------------------------------------------------------------------------------------*/
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { ISharedProcess } from 'vs/platform/windows/electron-main/windows';
export const ISharedProcessMainService = createDecorator<ISharedProcessMainService>('sharedProcessMainService');
@@ -15,6 +14,12 @@ export interface ISharedProcessMainService {
whenSharedProcessReady(): Promise<void>;
toggleSharedProcessWindow(): Promise<void>;
}
export interface ISharedProcess {
whenReady(): Promise<void>;
toggle(): void;
}
export class SharedProcessMainService implements ISharedProcessMainService {
_serviceBrand: undefined;
@@ -7,15 +7,16 @@ import { localize } from 'vs/nls';
import * as objects from 'vs/base/common/objects';
import { parseArgs, OPTIONS } from 'vs/platform/environment/node/argv';
import { IIssueService, IssueReporterData, IssueReporterFeatures, ProcessExplorerData } from 'vs/platform/issue/node/issue';
import { BrowserWindow, ipcMain, screen, dialog, IpcMainEvent, Display } from 'electron';
import { BrowserWindow, ipcMain, screen, Event as IpcMainEvent, Display, shell } from 'electron';
import { ILaunchMainService } from 'vs/platform/launch/electron-main/launchMainService';
import { PerformanceInfo, isRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnostics';
import { IDiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsService';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { isMacintosh, IProcessEnvironment } from 'vs/base/common/platform';
import { ILogService } from 'vs/platform/log/common/log';
import { IWindowState, IWindowsMainService } from 'vs/platform/windows/electron-main/windows';
import { IWindowState } from 'vs/platform/windows/electron-main/windows';
import { listProcesses } from 'vs/base/node/ps';
import { IDialogMainService } from 'vs/platform/dialogs/electron-main/dialogs';
const DEFAULT_BACKGROUND_COLOR = '#1E1E1E';
@@ -33,7 +34,7 @@ export class IssueMainService implements IIssueService {
@ILaunchMainService private readonly launchMainService: ILaunchMainService,
@ILogService private readonly logService: ILogService,
@IDiagnosticsService private readonly diagnosticsService: IDiagnosticsService,
@IWindowsMainService private readonly windowsMainService: IWindowsMainService
@IDialogMainService private readonly dialogMainService: IDialogMainService
) {
this.registerListeners();
}
@@ -89,7 +90,7 @@ export class IssueMainService implements IIssueService {
};
if (this._issueWindow) {
dialog.showMessageBox(this._issueWindow, messageOptions)
this.dialogMainService.showMessageBox(messageOptions, this._issueWindow)
.then(result => {
event.sender.send('vscode:issueReporterClipboardResponse', result.response === 0);
});
@@ -113,7 +114,7 @@ export class IssueMainService implements IIssueService {
};
if (this._issueWindow) {
dialog.showMessageBox(this._issueWindow, messageOptions)
this.dialogMainService.showMessageBox(messageOptions, this._issueWindow)
.then(result => {
if (result.response === 0) {
if (this._issueWindow) {
@@ -146,7 +147,7 @@ export class IssueMainService implements IIssueService {
});
ipcMain.on('vscode:openExternal', (_: unknown, arg: string) => {
this.windowsMainService.openExternal(arg);
shell.openExternal(arg);
});
ipcMain.on('vscode:closeIssueReporter', (event: IpcMainEvent) => {
@@ -141,6 +141,7 @@ export class LaunchMainService implements ILaunchMainService {
}
}
// Open new Window
if (openNewWindow) {
usedWindows = this.windowsMainService.open({
context,
@@ -150,8 +151,18 @@ export class LaunchMainService implements ILaunchMainService {
forceEmpty: true,
waitMarkerFileURI
});
} else {
usedWindows = [this.windowsMainService.focusLastActive(args, context)];
}
// Focus existing window or open if none opened
else {
const lastActive = this.windowsMainService.getLastActiveWindow();
if (lastActive) {
lastActive.focus();
usedWindows = [lastActive];
} else {
usedWindows = this.windowsMainService.open({ context, cli: args, forceEmpty: true });
}
}
}
@@ -216,7 +227,7 @@ export class LaunchMainService implements ILaunchMainService {
mainPID: process.pid,
mainArguments: process.argv.slice(1),
windows,
screenReader: !!app.accessibilitySupportEnabled,
screenReader: !!app.isAccessibilitySupportEnabled(),
gpuFeatureStatus: app.getGPUFeatureStatus()
});
}
+6
View File
@@ -87,4 +87,10 @@ export class BufferLogService extends AbstractLogService implements ILogService
this._logger.dispose();
}
}
flush(): void {
if (this._logger) {
this._logger.flush();
}
}
}
+1 -2
View File
@@ -78,8 +78,7 @@ export class FileLogService extends AbstractLogService implements ILogService {
}
}
flush(): Promise<void> {
return this.queue.queue(() => Promise.resolve());
flush(): void {
}
log(level: LogLevel, args: any[]): void {
+36 -2
View File
@@ -41,6 +41,11 @@ export interface ILogger extends IDisposable {
warn(message: string, ...args: any[]): void;
error(message: string | Error, ...args: any[]): void;
critical(message: string | Error, ...args: any[]): void;
/**
* An operation to flush the contents. Can be synchronous.
*/
flush(): void;
}
export interface ILogService extends ILogger {
@@ -69,6 +74,7 @@ export abstract class AbstractLogService extends Disposable {
getLevel(): LogLevel {
return this.level;
}
}
export class ConsoleLogMainService extends AbstractLogService implements ILogService {
@@ -145,6 +151,11 @@ export class ConsoleLogMainService extends AbstractLogService implements ILogSer
dispose(): void {
// noop
}
flush(): void {
// noop
}
}
export class ConsoleLogService extends AbstractLogService implements ILogService {
@@ -192,7 +203,13 @@ export class ConsoleLogService extends AbstractLogService implements ILogService
}
}
dispose(): void { }
dispose(): void {
// noop
}
flush(): void {
// noop
}
}
export class ConsoleLogInMainService extends AbstractLogService implements ILogService {
@@ -240,7 +257,13 @@ export class ConsoleLogInMainService extends AbstractLogService implements ILogS
}
}
dispose(): void { }
dispose(): void {
// noop
}
flush(): void {
// noop
}
}
export class MultiplexLogService extends AbstractLogService implements ILogService {
@@ -296,6 +319,12 @@ export class MultiplexLogService extends AbstractLogService implements ILogServi
}
}
flush(): void {
for (const logService of this.logServices) {
logService.flush();
}
}
dispose(): void {
for (const logService of this.logServices) {
logService.dispose();
@@ -346,6 +375,10 @@ export class DelegatedLogService extends Disposable implements ILogService {
critical(message: string | Error, ...args: any[]): void {
this.logService.critical(message, ...args);
}
flush(): void {
this.logService.flush();
}
}
export class NullLogService implements ILogService {
@@ -360,6 +393,7 @@ export class NullLogService implements ILogService {
error(message: string | Error, ...args: any[]): void { }
critical(message: string | Error, ...args: any[]): void { }
dispose(): void { }
flush(): void { }
}
export function getLogLevel(environmentService: IEnvironmentService): LogLevel {
+1 -1
View File
@@ -32,7 +32,7 @@ export class LoggerService extends Disposable implements ILoggerService {
if (resource.scheme === Schemas.file) {
const baseName = basename(resource);
const ext = extname(resource);
logger = new SpdLogService(baseName.substring(0, baseName.length - ext.length), dirname(resource).path, this.logService.getLevel());
logger = new SpdLogService(baseName.substring(0, baseName.length - ext.length), dirname(resource).fsPath, this.logService.getLevel());
} else {
logger = this.instantiationService.createInstance(FileLogService, basename(resource), resource, this.logService.getLevel());
}
+12 -7
View File
@@ -30,7 +30,7 @@ interface ILog {
message: string;
}
function log(logger: spdlog.RotatingLogger, level: LogLevel, message: string, sync: boolean): void {
function log(logger: spdlog.RotatingLogger, level: LogLevel, message: string): void {
switch (level) {
case LogLevel.Trace: logger.trace(message); break;
case LogLevel.Debug: logger.debug(message); break;
@@ -40,9 +40,6 @@ function log(logger: spdlog.RotatingLogger, level: LogLevel, message: string, sy
case LogLevel.Critical: logger.critical(message); break;
default: throw new Error('Invalid log level');
}
if (sync) {
logger.flush();
}
}
export class SpdLogService extends AbstractLogService implements ILogService {
@@ -53,7 +50,7 @@ export class SpdLogService extends AbstractLogService implements ILogService {
private _loggerCreationPromise: Promise<void> | undefined = undefined;
private _logger: spdlog.RotatingLogger | undefined;
constructor(private readonly name: string, private readonly logsFolder: string, level: LogLevel, private readonly sync: boolean = false) {
constructor(private readonly name: string, private readonly logsFolder: string, level: LogLevel) {
super();
this.setLevel(level);
this._createSpdLogLogger();
@@ -72,7 +69,7 @@ export class SpdLogService extends AbstractLogService implements ILogService {
this._logger = logger;
this._logger.setLevel(this.getLevel());
for (const { level, message } of this.buffer) {
log(this._logger, level, message, this.sync);
log(this._logger, level, message);
}
this.buffer = [];
}
@@ -83,7 +80,7 @@ export class SpdLogService extends AbstractLogService implements ILogService {
private _log(level: LogLevel, message: string): void {
if (this._logger) {
log(this._logger, level, message, this.sync);
log(this._logger, level, message);
} else if (this.getLevel() <= level) {
this.buffer.push({ level, message });
}
@@ -132,6 +129,14 @@ export class SpdLogService extends AbstractLogService implements ILogService {
}
}
flush(): void {
if (this._logger) {
this._logger.flush();
} else if (this._loggerCreationPromise) {
this._loggerCreationPromise.then(() => this.flush());
}
}
dispose(): void {
if (this._logger) {
this.disposeLogger();
@@ -6,7 +6,7 @@
import * as nls from 'vs/nls';
import { isMacintosh, language } from 'vs/base/common/platform';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { app, shell, Menu, MenuItem, BrowserWindow, MenuItemConstructorOptions, WebContents, Event, KeyboardEvent } from 'electron';
import { app, shell, Menu, MenuItem, BrowserWindow, MenuItemConstructorOptions, WebContents, Event, Event as KeyboardEvent } from 'electron';
import { OpenContext, IRunActionInWindowRequest, getTitleBarStyle, IRunKeybindingInWindowRequest, IWindowOpenable } from 'vs/platform/windows/common/windows';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
@@ -368,13 +368,13 @@ export class Menubar {
const servicesMenu = new Menu();
const services = new MenuItem({ label: nls.localize('mServices', "Services"), role: 'services', submenu: servicesMenu });
const hide = new MenuItem({ label: nls.localize('mHide', "Hide {0}", product.nameLong), role: 'hide', accelerator: 'Command+H' });
const hideOthers = new MenuItem({ label: nls.localize('mHideOthers', "Hide Others"), role: 'hideOthers', accelerator: 'Command+Alt+H' });
const hideOthers = new MenuItem({ label: nls.localize('mHideOthers', "Hide Others"), role: 'hideothers', accelerator: 'Command+Alt+H' });
const showAll = new MenuItem({ label: nls.localize('mShowAll', "Show All"), role: 'unhide' });
const quit = new MenuItem(this.likeAction('workbench.action.quit', {
label: nls.localize('miQuit', "Quit {0}", product.nameLong), click: () => {
if (
this.windowsMainService.getWindowCount() === 0 || // allow to quit when no more windows are open
!!this.windowsMainService.getFocusedWindow() || // allow to quit when window has focus (fix for https://github.com/Microsoft/vscode/issues/39191)
this.windowsMainService.getWindowCount() === 0 || // allow to quit when no more windows are open
!!BrowserWindow.getFocusedWindow() || // allow to quit when window has focus (fix for https://github.com/Microsoft/vscode/issues/39191)
this.windowsMainService.getLastActiveWindow()!.isMinimized() // allow to quit when window has no focus but is minimized (https://github.com/Microsoft/vscode/issues/63000)
) {
this.windowsMainService.quit();
@@ -557,7 +557,7 @@ export class Menubar {
label: this.mnemonicLabel(nls.localize('miCheckForUpdates', "Check for &&Updates...")), click: () => setTimeout(() => {
this.reportMenuActionTelemetry('CheckForUpdate');
const focusedWindow = this.windowsMainService.getFocusedWindow();
const focusedWindow = BrowserWindow.getFocusedWindow();
const context = focusedWindow ? { windowId: focusedWindow.id } : null;
this.updateService.checkForUpdates(context);
}, 0)
@@ -697,15 +697,16 @@ export class Menubar {
private makeContextAwareClickHandler(click: () => void, contextSpecificHandlers: IMenuItemClickHandler): () => void {
return () => {
// No Active Window
const activeWindow = this.windowsMainService.getFocusedWindow();
const activeWindow = BrowserWindow.getFocusedWindow();
if (!activeWindow) {
return contextSpecificHandlers.inNoWindow();
}
// DevTools focused
if (activeWindow.win.webContents.isDevToolsFocused()) {
return contextSpecificHandlers.inDevTools(activeWindow.win.webContents.devToolsWebContents);
if (activeWindow.webContents.isDevToolsFocused()) {
return contextSpecificHandlers.inDevTools(activeWindow.webContents.devToolsWebContents);
}
// Finally execute command in Window
@@ -719,14 +720,15 @@ export class Menubar {
// https://github.com/Microsoft/vscode/issues/11928
// Still allow to run when the last active window is minimized though for
// https://github.com/Microsoft/vscode/issues/63000
let activeWindow = this.windowsMainService.getFocusedWindow();
if (!activeWindow) {
let activeBrowserWindow = BrowserWindow.getFocusedWindow();
if (!activeBrowserWindow) {
const lastActiveWindow = this.windowsMainService.getLastActiveWindow();
if (lastActiveWindow && lastActiveWindow.isMinimized()) {
activeWindow = lastActiveWindow;
activeBrowserWindow = lastActiveWindow.win;
}
}
const activeWindow = activeBrowserWindow ? this.windowsMainService.getWindowById(activeBrowserWindow.id) : undefined;
if (activeWindow) {
this.logService.trace('menubar#runActionInRenderer', invocation);
+1 -1
View File
@@ -27,7 +27,7 @@ export function extractLocalHostUriMetaDataForPortMapping(uri: URI): { address:
if (uri.scheme !== 'http' && uri.scheme !== 'https') {
return undefined;
}
const localhostMatch = /^(localhost|127\.0\.0\.1):(\d+)$/.exec(uri.authority);
const localhostMatch = /^(localhost|127\.0\.0\.1|0\.0\.0\.0):(\d+)$/.exec(uri.authority);
if (!localhostMatch) {
return undefined;
}
@@ -70,6 +70,7 @@ class TestableLogService extends AbstractLogService implements ILogService {
}
dispose(): void { }
flush(): void { }
}
suite('AIAdapter', () => {
@@ -198,4 +199,4 @@ suite('AIAdapter', () => {
}
}]));
});
});
});
@@ -63,6 +63,12 @@ export function registerConfiguration(): IDisposable {
$ref: ignoredSettingsSchemaId,
additionalProperties: true,
uniqueItems: true
},
'configurationSync.enableAuth': {
'type': 'boolean',
description: localize('configurationSync.enableAuth', "Enables authentication and requires VS Code restart when changed"),
'default': false,
'scope': ConfigurationScope.APPLICATION
}
}
});
@@ -44,4 +44,8 @@ export class UserDataSyncLogService extends AbstractLogService implements IUserD
this.logger.critical(message, ...args);
}
flush(): void {
this.logger.flush();
}
}
@@ -131,27 +131,32 @@ export class UserDataAutoSync extends Disposable {
@IAuthTokenService private readonly authTokenService: IAuthTokenService,
) {
super();
this.updateEnablement();
this.sync(true);
this._register(Event.any<any>(authTokenService.onDidChangeStatus, userDataSyncService.onDidChangeStatus)(() => this.updateEnablement()));
this._register(Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('configurationSync.enable'))(() => this.updateEnablement()));
this.updateEnablement(false);
this._register(Event.any<any>(authTokenService.onDidChangeStatus, userDataSyncService.onDidChangeStatus)(() => this.updateEnablement(true)));
this._register(Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('configurationSync.enable'))(() => this.updateEnablement(true)));
// Sync immediately if there is a local change.
this._register(Event.debounce(this.userDataSyncService.onDidChangeLocal, () => undefined, 500)(() => this.sync(false)));
}
private updateEnablement(): void {
private updateEnablement(stopIfDisabled: boolean): void {
const enabled = this.isSyncEnabled();
if (this.enabled !== enabled) {
this.enabled = enabled;
if (this.enabled) {
this.userDataSyncLogService.info('Syncing configuration started');
this.sync(true);
} else {
if (this.enabled === enabled) {
return;
}
this.enabled = enabled;
if (this.enabled) {
this.userDataSyncLogService.info('Syncing configuration started');
this.sync(true);
return;
} else {
if (stopIfDisabled) {
this.userDataSyncService.stop();
this.userDataSyncLogService.info('Syncing configuration stopped.');
}
}
}
private async sync(loop: boolean): Promise<void> {
+6 -4
View File
@@ -19,15 +19,17 @@ export interface IOpenedWindow {
filename?: string;
}
export interface IOpenInWindowOptions {
forceNewWindow?: boolean;
export interface IBaseOpenWindowsOptions {
forceReuseWindow?: boolean;
}
export interface IOpenWindowOptions extends IBaseOpenWindowsOptions {
forceNewWindow?: boolean;
noRecentEntry?: boolean;
}
export interface IOpenEmptyWindowOptions {
reuse?: boolean;
export interface IOpenEmptyWindowOptions extends IBaseOpenWindowsOptions {
remoteAuthority?: string;
}
@@ -9,10 +9,10 @@ import { ParsedArgs } from 'vs/platform/environment/common/environment';
import { Event } from 'vs/base/common/event';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IProcessEnvironment } from 'vs/base/common/platform';
import { IWorkspaceIdentifier, IEnterWorkspaceResult } from 'vs/platform/workspaces/common/workspaces';
import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
import { ISerializableCommandAction } from 'vs/platform/actions/common/actions';
import { URI } from 'vs/base/common/uri';
import { MessageBoxReturnValue, SaveDialogReturnValue, OpenDialogReturnValue, Rectangle, BrowserWindow, MessageBoxOptions, SaveDialogOptions, OpenDialogOptions } from 'electron';
import { Rectangle, BrowserWindow } from 'electron';
export interface IWindowState {
width?: number;
@@ -86,38 +86,34 @@ export interface IWindowsCountChangedEvent {
}
export interface IWindowsMainService {
_serviceBrand: undefined;
// events
readonly onWindowReady: Event<ICodeWindow>;
readonly onWindowsCountChanged: Event<IWindowsCountChangedEvent>;
readonly onWindowClose: Event<number>;
// methods
reload(win: ICodeWindow, cli?: ParsedArgs): void;
enterWorkspace(win: ICodeWindow, path: URI): Promise<IEnterWorkspaceResult | undefined>;
closeWorkspace(win: ICodeWindow): void;
open(openConfig: IOpenConfiguration): ICodeWindow[];
openExtensionDevelopmentHostWindow(extensionDevelopmentPath: string[], openConfig: IOpenConfiguration): void;
openEmptyWindow(context: OpenContext, options?: IOpenEmptyWindowOptions): ICodeWindow[];
openExtensionDevelopmentHostWindow(extensionDevelopmentPath: string[], openConfig: IOpenConfiguration): ICodeWindow[];
pickFileFolderAndOpen(options: INativeOpenDialogOptions, win?: ICodeWindow): Promise<void>;
pickFolderAndOpen(options: INativeOpenDialogOptions, win?: ICodeWindow): Promise<void>;
pickFileAndOpen(options: INativeOpenDialogOptions, win?: ICodeWindow): Promise<void>;
pickWorkspaceAndOpen(options: INativeOpenDialogOptions, win?: ICodeWindow): Promise<void>;
showMessageBox(options: MessageBoxOptions, win?: ICodeWindow): Promise<MessageBoxReturnValue>;
showSaveDialog(options: SaveDialogOptions, win?: ICodeWindow): Promise<SaveDialogReturnValue>;
showOpenDialog(options: OpenDialogOptions, win?: ICodeWindow): Promise<OpenDialogReturnValue>;
focusLastActive(cli: ParsedArgs, context: OpenContext): ICodeWindow;
getLastActiveWindow(): ICodeWindow | undefined;
waitForWindowCloseOrLoad(windowId: number): Promise<void>;
openEmptyWindow(context: OpenContext, options?: IOpenEmptyWindowOptions): ICodeWindow[];
openNewTabbedWindow(context: OpenContext): ICodeWindow[];
openExternal(url: string): Promise<boolean>;
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;
waitForWindowCloseOrLoad(windowId: number): Promise<void>;
reload(win: ICodeWindow, cli?: ParsedArgs): void;
closeWorkspace(win: ICodeWindow): void;
quit(): void;
}
@@ -139,8 +135,3 @@ export interface IOpenConfiguration {
readonly initialStartup?: boolean;
readonly noRecentEntry?: boolean;
}
export interface ISharedProcess {
whenReady(): Promise<void>;
toggle(): void;
}
+126 -2
View File
@@ -3,12 +3,136 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IOpenInWindowOptions } from 'vs/platform/windows/common/windows';
import { OpenContext, IOpenWindowOptions } from 'vs/platform/windows/common/windows';
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 { isEqual, isEqualOrParent } from 'vs/base/common/resources';
export interface INativeOpenInWindowOptions extends IOpenInWindowOptions {
export interface INativeOpenWindowOptions extends IOpenWindowOptions {
diffMode?: boolean;
addMode?: boolean;
gotoLineMode?: boolean;
waitMarkerFileURI?: URI;
}
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;
userHome?: string;
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 => isEqualOrParent(fileUri, folder.uri))) {
return window;
}
} else {
// use the config path instead
if (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 && 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.filter(window => window.lastFocusTime === lastFocusedDate)[0];
}
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 && 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 && 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 && isEqual(window.openedWorkspace.configPath, uri)) {
return window;
}
// check for folder path
if (window.openedFolderUri && isEqual(window.openedFolderUri, uri)) {
return window;
}
}
return null;
}
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import * as path from 'vs/base/common/path';
import { findBestWindowOrFolderForFile, ISimpleWindow, IBestWindowOrFolderOptions } from 'vs/code/node/windowsFinder';
import { IBestWindowOrFolderOptions, IWindowContext, findBestWindowOrFolderForFile } from 'vs/platform/windows/node/window';
import { OpenContext } from 'vs/platform/windows/common/windows';
import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
import { toWorkspaceFolders } from 'vs/platform/workspace/common/workspace';
@@ -20,7 +20,7 @@ const testWorkspace: IWorkspaceIdentifier = {
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<ISimpleWindow>>): IBestWindowOrFolderOptions<ISimpleWindow> {
function options(custom?: Partial<IBestWindowOrFolderOptions<IWindowContext>>): IBestWindowOrFolderOptions<IWindowContext> {
return {
windows: [],
newWindow: false,
@@ -31,10 +31,10 @@ function options(custom?: Partial<IBestWindowOrFolderOptions<ISimpleWindow>>): I
};
}
const vscodeFolderWindow: ISimpleWindow = { lastFocusTime: 1, openedFolderUri: URI.file(path.join(fixturesFolder, 'vscode_folder')) };
const lastActiveWindow: ISimpleWindow = { lastFocusTime: 3, openedFolderUri: undefined };
const noVscodeFolderWindow: ISimpleWindow = { lastFocusTime: 2, openedFolderUri: URI.file(path.join(fixturesFolder, 'no_vscode_folder')) };
const windows: ISimpleWindow[] = [
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,
@@ -102,7 +102,7 @@ suite('WindowsFinder', () => {
windows,
fileUri: URI.file(path.join(fixturesFolder, 'vscode_folder', 'file.txt'))
})), vscodeFolderWindow);
const window: ISimpleWindow = { lastFocusTime: 1, openedFolderUri: URI.file(path.join(fixturesFolder, 'vscode_folder', 'nested_folder')) };
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'))
@@ -110,8 +110,8 @@ suite('WindowsFinder', () => {
});
test('More specific existing window wins', () => {
const window: ISimpleWindow = { lastFocusTime: 2, openedFolderUri: URI.file(path.join(fixturesFolder, 'no_vscode_folder')) };
const nestedFolderWindow: ISimpleWindow = { lastFocusTime: 1, openedFolderUri: URI.file(path.join(fixturesFolder, 'no_vscode_folder', 'nested_folder')) };
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'))
@@ -119,7 +119,7 @@ suite('WindowsFinder', () => {
});
test('Workspace folder wins', () => {
const window: ISimpleWindow = { lastFocusTime: 1, openedWorkspace: testWorkspace };
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'))
@@ -31,7 +31,7 @@ export interface IWorkspacesService {
_serviceBrand: undefined;
// Management
enterWorkspace(path: URI): Promise<IEnterWorkspaceResult | undefined>;
enterWorkspace(path: URI): Promise<IEnterWorkspaceResult | null>;
createUntitledWorkspace(folders?: IWorkspaceFolderCreationData[], remoteAuthority?: string): Promise<IWorkspaceIdentifier>;
deleteUntitledWorkspace(workspace: IWorkspaceIdentifier): Promise<void>;
getWorkspaceIdentifier(workspacePath: URI): Promise<IWorkspaceIdentifier>;
@@ -23,6 +23,7 @@ import { getSimpleWorkspaceLabel } from 'vs/platform/label/common/label';
import { exists } from 'vs/base/node/pfs';
import { ILifecycleMainService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { Disposable } from 'vs/base/common/lifecycle';
export const IWorkspacesHistoryMainService = createDecorator<IWorkspacesHistoryMainService>('workspacesHistoryMainService');
@@ -40,7 +41,7 @@ export interface IWorkspacesHistoryMainService {
updateWindowsJumpList(): void;
}
export class WorkspacesHistoryMainService implements IWorkspacesHistoryMainService {
export class WorkspacesHistoryMainService extends Disposable implements IWorkspacesHistoryMainService {
private static readonly MAX_TOTAL_RECENT_ENTRIES = 100;
@@ -60,18 +61,27 @@ export class WorkspacesHistoryMainService implements IWorkspacesHistoryMainServi
private readonly _onRecentlyOpenedChange = new Emitter<void>();
readonly onRecentlyOpenedChange: CommonEvent<void> = this._onRecentlyOpenedChange.event;
private macOSRecentDocumentsUpdater: ThrottledDelayer<void>;
private macOSRecentDocumentsUpdater = this._register(new ThrottledDelayer<void>(800));
constructor(
@IStateService private readonly stateService: IStateService,
@ILogService private readonly logService: ILogService,
@IWorkspacesMainService private readonly workspacesMainService: IWorkspacesMainService,
@IEnvironmentService private readonly environmentService: IEnvironmentService,
@ILifecycleMainService lifecycleMainService: ILifecycleMainService
@ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService
) {
this.macOSRecentDocumentsUpdater = new ThrottledDelayer<void>(800);
super();
lifecycleMainService.when(LifecycleMainPhase.AfterWindowOpen).then(() => this.handleWindowsJumpList());
this.registerListeners();
}
private registerListeners(): void {
// Install window jump list after opening window
this.lifecycleMainService.when(LifecycleMainPhase.AfterWindowOpen).then(() => this.handleWindowsJumpList());
// Add to history when entering workspace
this._register(this.workspacesMainService.onWorkspaceEntered(event => this.addRecentlyOpened([{ workspace: event.workspace }])));
}
private handleWindowsJumpList(): void {
@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IWorkspaceIdentifier, hasWorkspaceFileExtension, UNTITLED_WORKSPACE_NAME, IResolvedWorkspace, IStoredWorkspaceFolder, isStoredWorkspaceFolder, IWorkspaceFolderCreationData, IUntitledWorkspaceInfo, getStoredWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces';
import { IWorkspaceIdentifier, hasWorkspaceFileExtension, UNTITLED_WORKSPACE_NAME, IResolvedWorkspace, IStoredWorkspaceFolder, isStoredWorkspaceFolder, IWorkspaceFolderCreationData, IUntitledWorkspaceInfo, getStoredWorkspaceFolder, IEnterWorkspaceResult } from 'vs/platform/workspaces/common/workspaces';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { join, dirname } from 'vs/base/common/path';
import { mkdirp, writeFile, rimrafSync, readdirSync, writeFileSync } from 'vs/base/node/pfs';
@@ -17,31 +17,43 @@ import { toWorkspaceFolders } from 'vs/platform/workspace/common/workspace';
import { URI } from 'vs/base/common/uri';
import { Schemas } from 'vs/base/common/network';
import { Disposable } from 'vs/base/common/lifecycle';
import { originalFSPath, isEqualOrParent, joinPath } from 'vs/base/common/resources';
import { originalFSPath, isEqualOrParent, joinPath, isEqual, basename } from 'vs/base/common/resources';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { ICodeWindow } from 'vs/platform/windows/electron-main/windows';
import { localize } from 'vs/nls';
import product from 'vs/platform/product/common/product';
import { MessageBoxOptions, BrowserWindow } from 'electron';
import { withNullAsUndefined } from 'vs/base/common/types';
import { IBackupMainService } from 'vs/platform/backup/electron-main/backup';
import { IDialogMainService } from 'vs/platform/dialogs/electron-main/dialogs';
import { findWindowOnWorkspace } from 'vs/platform/windows/node/window';
export const IWorkspacesMainService = createDecorator<IWorkspacesMainService>('workspacesMainService');
export interface IWorkspaceEnteredEvent {
window: ICodeWindow;
workspace: IWorkspaceIdentifier;
}
export interface IWorkspacesMainService {
_serviceBrand: undefined;
readonly onUntitledWorkspaceDeleted: Event<IWorkspaceIdentifier>;
readonly onWorkspaceEntered: Event<IWorkspaceEnteredEvent>;
enterWorkspace(intoWindow: ICodeWindow, openedWindows: ICodeWindow[], path: URI): Promise<IEnterWorkspaceResult | null>;
createUntitledWorkspace(folders?: IWorkspaceFolderCreationData[], remoteAuthority?: string): Promise<IWorkspaceIdentifier>;
createUntitledWorkspaceSync(folders?: IWorkspaceFolderCreationData[]): IWorkspaceIdentifier;
resolveLocalWorkspaceSync(path: URI): IResolvedWorkspace | null;
isUntitledWorkspace(workspace: IWorkspaceIdentifier): boolean;
deleteUntitledWorkspace(workspace: IWorkspaceIdentifier): Promise<void>;
deleteUntitledWorkspaceSync(workspace: IWorkspaceIdentifier): void;
getUntitledWorkspacesSync(): IUntitledWorkspaceInfo[];
isUntitledWorkspace(workspace: IWorkspaceIdentifier): boolean;
createUntitledWorkspace(folders?: IWorkspaceFolderCreationData[], remoteAuthority?: string): Promise<IWorkspaceIdentifier>;
deleteUntitledWorkspace(workspace: IWorkspaceIdentifier): Promise<void>;
resolveLocalWorkspaceSync(path: URI): IResolvedWorkspace | null;
getWorkspaceIdentifier(workspacePath: URI): Promise<IWorkspaceIdentifier>;
}
@@ -59,9 +71,14 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain
private readonly _onUntitledWorkspaceDeleted = this._register(new Emitter<IWorkspaceIdentifier>());
readonly onUntitledWorkspaceDeleted: Event<IWorkspaceIdentifier> = this._onUntitledWorkspaceDeleted.event;
private readonly _onWorkspaceEntered = this._register(new Emitter<IWorkspaceEnteredEvent>());
readonly onWorkspaceEntered: Event<IWorkspaceEnteredEvent> = this._onWorkspaceEntered.event;
constructor(
@IEnvironmentService private readonly environmentService: IEnvironmentService,
@ILogService private readonly logService: ILogService
@ILogService private readonly logService: ILogService,
@IBackupMainService private readonly backupMainService: IBackupMainService,
@IDialogMainService private readonly dialogMainService: IDialogMainService
) {
super();
@@ -236,6 +253,74 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain
}
return untitledWorkspaces;
}
async enterWorkspace(window: ICodeWindow, windows: ICodeWindow[], path: URI): Promise<IEnterWorkspaceResult | null> {
if (!window || !window.win || !window.isReady) {
return null; // return early if the window is not ready or disposed
}
const isValid = await this.isValidTargetWorkspacePath(window, windows, path);
if (!isValid) {
return null; // return early if the workspace is not valid
}
const result = this.doEnterWorkspace(window, getWorkspaceIdentifier(path));
// Emit as event
this._onWorkspaceEntered.fire({ window, workspace: result.workspace });
return result;
}
private async isValidTargetWorkspacePath(window: ICodeWindow, windows: ICodeWindow[], path?: URI): Promise<boolean> {
if (!path) {
return true;
}
if (window.openedWorkspace && isEqual(window.openedWorkspace.configPath, path)) {
return false; // window is already opened on a workspace with that path
}
// Prevent overwriting a workspace that is currently opened in another window
if (findWindowOnWorkspace(windows, getWorkspaceIdentifier(path))) {
const options: MessageBoxOptions = {
title: product.nameLong,
type: 'info',
buttons: [localize('ok', "OK")],
message: localize('workspaceOpenedMessage', "Unable to save workspace '{0}'", basename(path)),
detail: localize('workspaceOpenedDetail', "The workspace is already opened in another window. Please close that window first and then try again."),
noLink: true
};
await this.dialogMainService.showMessageBox(options, withNullAsUndefined(BrowserWindow.getFocusedWindow()));
return false;
}
return true; // OK
}
private doEnterWorkspace(window: ICodeWindow, workspace: IWorkspaceIdentifier): IEnterWorkspaceResult {
window.focus();
// Register window for backups and migrate current backups over
let backupPath: string | undefined;
if (!window.config.extensionDevelopmentPath) {
backupPath = this.backupMainService.registerWorkspaceBackupSync({ workspace, remoteAuthority: window.remoteAuthority }, window.config.backupPath);
}
// if the window was opened on an untitled workspace, delete it.
if (window.openedWorkspace && this.isUntitledWorkspace(window.openedWorkspace)) {
this.deleteUntitledWorkspaceSync(window.openedWorkspace);
}
// Update window configuration properly based on transition to workspace
window.config.folderUri = undefined;
window.config.workspace = workspace;
window.config.backupPath = backupPath;
return { workspace, backupPath };
}
}
function getWorkspaceId(configPath: URI): string {
@@ -23,13 +23,13 @@ export class WorkspacesService implements AddFirstParameterToFunctions<IWorkspac
//#region Workspace Management
async enterWorkspace(windowId: number, path: URI): Promise<IEnterWorkspaceResult | undefined> {
async enterWorkspace(windowId: number, path: URI): Promise<IEnterWorkspaceResult | null> {
const window = this.windowsMainService.getWindowById(windowId);
if (window) {
return this.windowsMainService.enterWorkspace(window, path);
return this.workspacesMainService.enterWorkspace(window, this.windowsMainService.getWindows(), path);
}
return undefined;
return null;
}
createUntitledWorkspace(windowId: number, folders?: IWorkspaceFolderCreationData[], remoteAuthority?: string): Promise<IWorkspaceIdentifier> {
@@ -18,6 +18,7 @@ import { getRandomTestPath } from 'vs/base/test/node/testUtils';
import { isWindows } from 'vs/base/common/platform';
import { normalizeDriveLetter } from 'vs/base/common/labels';
import { dirname, joinPath } from 'vs/base/common/resources';
import { TestBackupMainService, TestDialogMainService } from 'vs/workbench/test/workbenchTestServices';
suite('WorkspacesMainService', () => {
const parentDir = getRandomTestPath(os.tmpdir(), 'vsctests', 'workspacesservice');
@@ -43,7 +44,7 @@ suite('WorkspacesMainService', () => {
let service: WorkspacesMainService;
setup(async () => {
service = new WorkspacesMainService(environmentService, logService);
service = new WorkspacesMainService(environmentService, logService, new TestBackupMainService(), new TestDialogMainService());
// Delete any existing backups completely and then re-create it.
await pfs.rimraf(untitledWorkspacesHomePath, pfs.RimRafMode.MOVE);
+1 -39
View File
@@ -1085,45 +1085,7 @@ declare module 'vscode' {
//#region Custom editors, mjbvz
export enum WebviewContentState {
/**
* The webview content cannot be modified.
*
* This disables save.
*/
Readonly = 1,
/**
* The webview content has not been changed but they can be modified and saved.
*/
Unchanged = 2,
/**
* The webview content has been changed and can be saved.
*/
Dirty = 3,
}
export interface WebviewEditorState {
readonly contentState: WebviewContentState;
}
export interface WebviewPanel {
editorState: WebviewEditorState;
/**
* Fired when the webview is being saved.
*
* Both `Unchanged` and `Dirty` editors can be saved.
*
* Extensions should call `waitUntil` to signal when the save operation complete
*/
readonly onWillSave: Event<{ waitUntil: (thenable: Thenable<boolean>) => void }>;
}
export interface WebviewEditor extends WebviewPanel {
// TODO: We likely do not want `editorState` and `onWillSave` enabled for
// resource backed webviews
}
export interface WebviewEditorProvider {
@@ -1168,7 +1130,7 @@ declare module 'vscode' {
*
* @return A uri that can be used on the client machine.
*/
export function resolveExternalUri(target: Uri): Thenable<Uri>;
export function asExternalUri(target: Uri): Thenable<Uri>;
}
//#endregion
@@ -11,7 +11,7 @@ import * as search from 'vs/workbench/contrib/search/common/search';
import { CancellationToken } from 'vs/base/common/cancellation';
import { Position as EditorPosition } from 'vs/editor/common/core/position';
import { Range as EditorRange, IRange } from 'vs/editor/common/core/range';
import { ExtHostContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, MainContext, IExtHostContext, ILanguageConfigurationDto, IRegExpDto, IIndentationRuleDto, IOnEnterRuleDto, ILocationDto, IWorkspaceSymbolDto, reviveWorkspaceEditDto, IDocumentFilterDto, IDefinitionLinkDto, ISignatureHelpProviderMetadataDto, ILinkDto, ICallHierarchyItemDto, ISuggestDataDto, ICodeActionDto } from '../common/extHost.protocol';
import { ExtHostContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, MainContext, IExtHostContext, ILanguageConfigurationDto, IRegExpDto, IIndentationRuleDto, IOnEnterRuleDto, ILocationDto, IWorkspaceSymbolDto, reviveWorkspaceEditDto, IDocumentFilterDto, IDefinitionLinkDto, ISignatureHelpProviderMetadataDto, ILinkDto, ICallHierarchyItemDto, ISuggestDataDto, ICodeActionDto, ISuggestDataDtoField } from '../common/extHost.protocol';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { LanguageConfiguration, IndentationRule, OnEnterRule } from 'vs/editor/common/modes/languageConfiguration';
import { IModeService } from 'vs/editor/common/services/modeService';
@@ -328,20 +328,20 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
private static _inflateSuggestDto(defaultRange: IRange, data: ISuggestDataDto): modes.CompletionItem {
return {
label: data.a,
kind: data.b,
tags: data.n,
detail: data.c,
documentation: data.d,
sortText: data.e,
filterText: data.f,
preselect: data.g,
insertText: typeof data.h === 'undefined' ? data.a : data.h,
insertTextRules: data.i,
range: data.j || defaultRange,
commitCharacters: data.k,
additionalTextEdits: data.l,
command: data.m,
label: data[ISuggestDataDtoField.label],
kind: data[ISuggestDataDtoField.kind],
tags: data[ISuggestDataDtoField.kindModifier],
detail: data[ISuggestDataDtoField.detail],
documentation: data[ISuggestDataDtoField.documentation],
sortText: data[ISuggestDataDtoField.sortText],
filterText: data[ISuggestDataDtoField.filterText],
preselect: data[ISuggestDataDtoField.preselect],
insertText: typeof data.h === 'undefined' ? data[ISuggestDataDtoField.label] : data.h,
insertTextRules: data[ISuggestDataDtoField.insertTextRules],
range: data[ISuggestDataDtoField.range] || defaultRange,
commitCharacters: data[ISuggestDataDtoField.commitCharacters],
additionalTextEdits: data[ISuggestDataDtoField.additionalTextEdits],
command: data[ISuggestDataDtoField.command],
// not-standard
_id: data.x,
};
@@ -47,7 +47,7 @@ export class MainThreadWindow implements MainThreadWindowShape {
return this.openerService.open(uri, { openExternal: true, allowTunneling: options.allowTunneling });
}
async $resolveExternalUri(uriComponents: UriComponents, options: IOpenUriOptions): Promise<UriComponents> {
async $asExternalUri(uriComponents: UriComponents, options: IOpenUriOptions): Promise<UriComponents> {
const uri = URI.revive(uriComponents);
const result = await this.openerService.resolveExternalUri(uri, options);
return result.resolved;
+7 -5
View File
@@ -11,7 +11,7 @@ import { ITextEditorOptions } from 'vs/platform/editor/common/editor';
import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor';
import { EditorGroupLayout } from 'vs/workbench/services/editor/common/editorGroupsService';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { IOpenInWindowOptions, IWindowOpenable } from 'vs/platform/windows/common/windows';
import { IOpenWindowOptions, IWindowOpenable, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows';
import { IWorkspacesService, hasWorkspaceFileExtension, IRecent } from 'vs/platform/workspaces/common/workspaces';
import { Schemas } from 'vs/base/common/network';
@@ -49,7 +49,7 @@ export class OpenFolderAPICommand {
if (!uri) {
return executor.executeCommand('_files.pickFolderAndOpen', { forceNewWindow: arg.forceNewWindow });
}
const options: IOpenInWindowOptions = { forceNewWindow: arg.forceNewWindow, forceReuseWindow: arg.forceReuseWindow, noRecentEntry: arg.noRecentEntry };
const options: IOpenWindowOptions = { forceNewWindow: arg.forceNewWindow, forceReuseWindow: arg.forceReuseWindow, noRecentEntry: arg.noRecentEntry };
uri = URI.revive(uri);
const uriToOpen: IWindowOpenable = (hasWorkspaceFileExtension(uri) || uri.scheme === Schemas.untitled) ? { workspaceUri: uri } : { folderUri: uri };
return executor.executeCommand('_files.windowOpen', [uriToOpen], options);
@@ -75,10 +75,12 @@ interface INewWindowAPICommandOptions {
export class NewWindowAPICommand {
public static ID = 'vscode.newWindow';
public static execute(executor: ICommandsExecutor, options?: INewWindowAPICommandOptions): Promise<any> {
return executor.executeCommand('_files.newWindow', {
reuse: options && options.reuseWindow,
const commandOptions: IOpenEmptyWindowOptions = {
forceReuseWindow: options && options.reuseWindow,
remoteAuthority: options && options.remoteAuthority
});
};
return executor.executeCommand('_files.newWindow', commandOptions);
}
}
CommandsRegistry.registerCommand({
@@ -249,9 +249,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
openExternal(uri: URI) {
return extHostWindow.openUri(uri, { allowTunneling: !!initData.remote.isRemote });
},
resolveExternalUri(uri: URI) {
asExternalUri(uri: URI) {
checkProposedApiEnabled(extension);
return extHostWindow.resolveExternalUri(uri, { allowTunneling: !!initData.remote.isRemote });
return extHostWindow.asExternalUri(uri, { allowTunneling: !!initData.remote.isRemote });
},
get remoteName() {
return getRemoteName(initData.remote.authority);
+32 -15
View File
@@ -750,7 +750,7 @@ export interface IOpenUriOptions {
export interface MainThreadWindowShape extends IDisposable {
$getWindowVisibility(): Promise<boolean>;
$openUri(uri: UriComponents, options: IOpenUriOptions): Promise<boolean>;
$resolveExternalUri(uri: UriComponents, options: IOpenUriOptions): Promise<UriComponents>;
$asExternalUri(uri: UriComponents, options: IOpenUriOptions): Promise<UriComponents>;
}
// -- extension host
@@ -944,21 +944,38 @@ export class IdObject {
}
}
export const enum ISuggestDataDtoField {
label = 'a',
kind = 'b',
detail = 'c',
documentation = 'd',
sortText = 'e',
filterText = 'f',
preselect = 'g',
insertText = 'h',
insertTextRules = 'i',
range = 'j',
commitCharacters = 'k',
additionalTextEdits = 'l',
command = 'm',
kindModifier = 'n',
}
export interface ISuggestDataDto {
a/* label */: string;
b/* kind */: modes.CompletionItemKind;
c/* detail */?: string;
d/* documentation */?: string | IMarkdownString;
e/* sortText */?: string;
f/* filterText */?: string;
g/* preselect */?: boolean;
h/* insertText */?: string;
i/* insertTextRules */?: modes.CompletionItemInsertTextRule;
j/* range */?: IRange;
k/* commitCharacters */?: string[];
l/* additionalTextEdits */?: ISingleEditOperation[];
m/* command */?: modes.Command;
n/* kindModifier */?: modes.CompletionItemTag[];
[ISuggestDataDtoField.label]: string;
[ISuggestDataDtoField.kind]: modes.CompletionItemKind;
[ISuggestDataDtoField.detail]?: string;
[ISuggestDataDtoField.documentation]?: string | IMarkdownString;
[ISuggestDataDtoField.sortText]?: string;
[ISuggestDataDtoField.filterText]?: string;
[ISuggestDataDtoField.preselect]?: boolean;
[ISuggestDataDtoField.insertText]?: string;
[ISuggestDataDtoField.insertTextRules]?: modes.CompletionItemInsertTextRule;
[ISuggestDataDtoField.range]?: IRange;
[ISuggestDataDtoField.commitCharacters]?: string[];
[ISuggestDataDtoField.additionalTextEdits]?: ISingleEditOperation[];
[ISuggestDataDtoField.command]?: modes.Command;
[ISuggestDataDtoField.kindModifier]?: modes.CompletionItemTag[];
// not-standard
x?: ChainedCacheId;
}
@@ -327,6 +327,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio
}
this._logService.info(`ExtensionService#_doActivateExtension ${extensionDescription.identifier.value} ${JSON.stringify(reason)}`);
this._logService.flush();
const activationTimesBuilder = new ExtensionActivationTimesBuilder(reason.startup);
return Promise.all([
@@ -724,18 +724,18 @@ class SuggestAdapter {
//
x: id,
//
a: item.label,
b: typeConvert.CompletionItemKind.from(item.kind),
n: item.tags && item.tags.map(typeConvert.CompletionItemTag.from),
c: item.detail,
d: typeof item.documentation === 'undefined' ? undefined : typeConvert.MarkdownString.fromStrict(item.documentation),
e: item.sortText,
f: item.filterText,
g: item.preselect,
i: item.keepWhitespace ? modes.CompletionItemInsertTextRule.KeepWhitespace : 0,
k: item.commitCharacters,
l: item.additionalTextEdits && item.additionalTextEdits.map(typeConvert.TextEdit.from),
m: this._commands.toInternal(item.command, disposables),
[extHostProtocol.ISuggestDataDtoField.label]: item.label,
[extHostProtocol.ISuggestDataDtoField.kind]: typeConvert.CompletionItemKind.from(item.kind),
[extHostProtocol.ISuggestDataDtoField.kindModifier]: item.tags && item.tags.map(typeConvert.CompletionItemTag.from),
[extHostProtocol.ISuggestDataDtoField.detail]: item.detail,
[extHostProtocol.ISuggestDataDtoField.documentation]: typeof item.documentation === 'undefined' ? undefined : typeConvert.MarkdownString.fromStrict(item.documentation),
[extHostProtocol.ISuggestDataDtoField.sortText]: item.sortText,
[extHostProtocol.ISuggestDataDtoField.filterText]: item.filterText,
[extHostProtocol.ISuggestDataDtoField.preselect]: item.preselect,
[extHostProtocol.ISuggestDataDtoField.insertTextRules]: item.keepWhitespace ? modes.CompletionItemInsertTextRule.KeepWhitespace : 0,
[extHostProtocol.ISuggestDataDtoField.commitCharacters]: item.commitCharacters,
[extHostProtocol.ISuggestDataDtoField.additionalTextEdits]: item.additionalTextEdits && item.additionalTextEdits.map(typeConvert.TextEdit.from),
[extHostProtocol.ISuggestDataDtoField.command]: this._commands.toInternal(item.command, disposables),
};
// 'insertText'-logic
@@ -859,7 +859,7 @@ export namespace SignatureInformation {
return {
label: info.label,
documentation: info.documentation ? MarkdownString.fromStrict(info.documentation) : undefined,
parameters: info.parameters && info.parameters.map(ParameterInformation.from)
parameters: Array.isArray(info.parameters) ? info.parameters.map(ParameterInformation.from) : []
};
}
@@ -867,7 +867,7 @@ export namespace SignatureInformation {
return {
label: info.label,
documentation: htmlContent.isMarkdownString(info.documentation) ? MarkdownString.to(info.documentation) : info.documentation,
parameters: info.parameters && info.parameters.map(ParameterInformation.to)
parameters: Array.isArray(info.parameters) ? info.parameters.map(ParameterInformation.to) : []
};
}
}
@@ -878,7 +878,7 @@ export namespace SignatureHelp {
return {
activeSignature: help.activeSignature,
activeParameter: help.activeParameter,
signatures: help.signatures && help.signatures.map(SignatureInformation.from)
signatures: Array.isArray(help.signatures) ? help.signatures.map(SignatureInformation.from) : [],
};
}
@@ -886,7 +886,7 @@ export namespace SignatureHelp {
return {
activeSignature: help.activeSignature,
activeParameter: help.activeParameter,
signatures: help.signatures && help.signatures.map(SignatureInformation.to)
signatures: Array.isArray(help.signatures) ? help.signatures.map(SignatureInformation.to) : [],
};
}
}
@@ -1163,13 +1163,3 @@ export namespace LogLevel {
return types.LogLevel.Info;
}
}
export namespace WebviewContentState {
export function from(state: vscode.WebviewContentState): modes.WebviewContentState {
switch (state) {
case types.WebviewContentState.Readonly: return modes.WebviewContentState.Readonly;
case types.WebviewContentState.Unchanged: return modes.WebviewContentState.Unchanged;
case types.WebviewContentState.Dirty: return modes.WebviewContentState.Dirty;
default: throw new Error('Unknown vscode.WebviewContentState');
}
}
}

Some files were not shown because too many files have changed in this diff Show More