{
+ cursor.setSelections('test', [new Selection(1, 8, 1, 8)]);
+
+ cursor.executeEdits('snippet', [{ range: new Range(1, 6, 1, 8), text: 'id=""' }], () => [new Selection(1, 10, 1, 10)]);
+ assert.strictEqual(model.getLineContent(1), '
{
let mode = new AutoClosingMode();
usingCursor({
diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts
index f32c6bc9d8..e1a0dbd6d6 100644
--- a/src/vs/monaco.d.ts
+++ b/src/vs/monaco.d.ts
@@ -4058,7 +4058,7 @@ declare namespace monaco.editor {
* @param edits The edits to execute.
* @param endCursorState Cursor state after the edits were applied.
*/
- executeEdits(source: string, edits: IIdentifiedSingleEditOperation[], endCursorState?: Selection[]): boolean;
+ executeEdits(source: string, edits: IIdentifiedSingleEditOperation[], endCursorState?: ICursorStateComputer | Selection[]): boolean;
/**
* Execute multiple (concomitant) commands on the editor.
* @param source The source of the call.
diff --git a/src/vs/platform/clipboard/browser/clipboardService.ts b/src/vs/platform/clipboard/browser/clipboardService.ts
index ecb5a9279d..b077d4531b 100644
--- a/src/vs/platform/clipboard/browser/clipboardService.ts
+++ b/src/vs/platform/clipboard/browser/clipboardService.ts
@@ -15,10 +15,18 @@ export class BrowserClipboardService implements IClipboardService {
private _internalResourcesClipboard: URI[] | undefined;
async writeText(text: string, type?: string): Promise
{
+ if (type) {
+ return; // TODO@sbatten
+ }
+
return navigator.clipboard.writeText(text);
}
async readText(type?: string): Promise {
+ if (type) {
+ return ''; // TODO@sbatten
+ }
+
return navigator.clipboard.readText();
}
diff --git a/src/vs/platform/diagnostics/common/diagnosticsService.ts b/src/vs/platform/diagnostics/common/diagnostics.ts
similarity index 69%
rename from src/vs/platform/diagnostics/common/diagnosticsService.ts
rename to src/vs/platform/diagnostics/common/diagnostics.ts
index 678c7e7a09..3eb4e55f34 100644
--- a/src/vs/platform/diagnostics/common/diagnosticsService.ts
+++ b/src/vs/platform/diagnostics/common/diagnostics.ts
@@ -5,8 +5,6 @@
import { UriComponents } from 'vs/base/common/uri';
import { ProcessItem } from 'vs/base/common/processes';
-import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
-import { IMainProcessInfo } from 'vs/platform/launch/common/launchService';
import { IWorkspace } from 'vs/platform/workspace/common/workspace';
import { IStringDictionary } from 'vs/base/common/collections';
@@ -67,18 +65,6 @@ export interface IWorkspaceInformation extends IWorkspace {
telemetryId: string | undefined;
}
-export const ID = 'diagnosticsService';
-export const IDiagnosticsService = createDecorator(ID);
-
-export interface IDiagnosticsService {
- _serviceBrand: any;
-
- getPerformanceInfo(mainProcessInfo: IMainProcessInfo, remoteInfo: (IRemoteDiagnosticInfo | IRemoteDiagnosticError)[]): Promise;
- getSystemInfo(mainProcessInfo: IMainProcessInfo, remoteInfo: (IRemoteDiagnosticInfo | IRemoteDiagnosticError)[]): Promise;
- getDiagnostics(mainProcessInfo: IMainProcessInfo, remoteInfo: (IRemoteDiagnosticInfo | IRemoteDiagnosticError)[]): Promise;
- reportWorkspaceStats(workspace: IWorkspaceInformation): Promise;
-}
-
export function isRemoteDiagnosticError(x: any): x is IRemoteDiagnosticError {
return !!x.hostName && !!x.errorMessage;
}
diff --git a/src/vs/platform/diagnostics/node/diagnosticsIpc.ts b/src/vs/platform/diagnostics/node/diagnosticsIpc.ts
index f5699bab33..cdb3e6a7e8 100644
--- a/src/vs/platform/diagnostics/node/diagnosticsIpc.ts
+++ b/src/vs/platform/diagnostics/node/diagnosticsIpc.ts
@@ -4,7 +4,8 @@
*--------------------------------------------------------------------------------------------*/
import { IServerChannel, IChannel } from 'vs/base/parts/ipc/common/ipc';
-import { IDiagnosticsService, IRemoteDiagnosticInfo, IRemoteDiagnosticError, SystemInfo, PerformanceInfo } from 'vs/platform/diagnostics/common/diagnosticsService';
+import { IRemoteDiagnosticInfo, IRemoteDiagnosticError, SystemInfo, PerformanceInfo } from 'vs/platform/diagnostics/common/diagnostics';
+import { IDiagnosticsService } from './diagnosticsService';
import { Event } from 'vs/base/common/event';
import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
import { IMainProcessInfo } from 'vs/platform/launch/common/launchService';
diff --git a/src/vs/platform/diagnostics/node/diagnosticsService.ts b/src/vs/platform/diagnostics/node/diagnosticsService.ts
index b3a90f9eaf..7aa52a1fc5 100644
--- a/src/vs/platform/diagnostics/node/diagnosticsService.ts
+++ b/src/vs/platform/diagnostics/node/diagnosticsService.ts
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as osLib from 'os';
import { virtualMachineHint } from 'vs/base/node/id';
-import { IMachineInfo, WorkspaceStats, WorkspaceStatItem, IDiagnosticsService, PerformanceInfo, SystemInfo, IRemoteDiagnosticInfo, IRemoteDiagnosticError, isRemoteDiagnosticError, IWorkspaceInformation } from 'vs/platform/diagnostics/common/diagnosticsService';
+import { IMachineInfo, WorkspaceStats, WorkspaceStatItem, PerformanceInfo, SystemInfo, IRemoteDiagnosticInfo, IRemoteDiagnosticError, isRemoteDiagnosticError, IWorkspaceInformation } from 'vs/platform/diagnostics/common/diagnostics';
import { readdir, stat, exists, readFile } from 'fs';
import { join, basename } from 'vs/base/common/path';
import { parse, ParseError } from 'vs/base/common/json';
@@ -17,6 +17,19 @@ import { URI } from 'vs/base/common/uri';
import { ProcessItem } from 'vs/base/common/processes';
import { IMainProcessInfo } from 'vs/platform/launch/common/launchService';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
+import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
+
+export const ID = 'diagnosticsService';
+export const IDiagnosticsService = createDecorator(ID);
+
+export interface IDiagnosticsService {
+ _serviceBrand: any;
+
+ getPerformanceInfo(mainProcessInfo: IMainProcessInfo, remoteInfo: (IRemoteDiagnosticInfo | IRemoteDiagnosticError)[]): Promise;
+ getSystemInfo(mainProcessInfo: IMainProcessInfo, remoteInfo: (IRemoteDiagnosticInfo | IRemoteDiagnosticError)[]): Promise;
+ getDiagnostics(mainProcessInfo: IMainProcessInfo, remoteInfo: (IRemoteDiagnosticInfo | IRemoteDiagnosticError)[]): Promise;
+ reportWorkspaceStats(workspace: IWorkspaceInformation): Promise;
+}
export interface VersionInfo {
vscodeVersion: string;
diff --git a/src/vs/platform/driver/browser/baseDriver.ts b/src/vs/platform/driver/browser/baseDriver.ts
new file mode 100644
index 0000000000..0f4561468d
--- /dev/null
+++ b/src/vs/platform/driver/browser/baseDriver.ts
@@ -0,0 +1,192 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the Source EULA. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { getTopLeftOffset, getClientArea } from 'vs/base/browser/dom';
+import { coalesce } from 'vs/base/common/arrays';
+import { IElement, IWindowDriver } from 'vs/platform/driver/common/driver';
+
+function serializeElement(element: Element, recursive: boolean): IElement {
+ const attributes = Object.create(null);
+
+ for (let j = 0; j < element.attributes.length; j++) {
+ const attr = element.attributes.item(j);
+ if (attr) {
+ attributes[attr.name] = attr.value;
+ }
+ }
+
+ const children: IElement[] = [];
+
+ if (recursive) {
+ for (let i = 0; i < element.children.length; i++) {
+ const child = element.children.item(i);
+ if (child) {
+ children.push(serializeElement(child, true));
+ }
+ }
+ }
+
+ const { left, top } = getTopLeftOffset(element as HTMLElement);
+
+ return {
+ tagName: element.tagName,
+ className: element.className,
+ textContent: element.textContent || '',
+ attributes,
+ children,
+ left,
+ top
+ };
+}
+
+export abstract class BaseWindowDriver implements IWindowDriver {
+
+ constructor() { }
+
+ abstract click(selector: string, xoffset?: number, yoffset?: number): Promise;
+ abstract doubleClick(selector: string): Promise;
+
+ async setValue(selector: string, text: string): Promise {
+ const element = document.querySelector(selector);
+
+ if (!element) {
+ return Promise.reject(new Error(`Element not found: ${selector}`));
+ }
+
+ const inputElement = element as HTMLInputElement;
+ inputElement.value = text;
+
+ const event = new Event('input', { bubbles: true, cancelable: true });
+ inputElement.dispatchEvent(event);
+ }
+
+ async getTitle(): Promise {
+ return document.title;
+ }
+
+ async isActiveElement(selector: string): Promise {
+ const element = document.querySelector(selector);
+
+ if (element !== document.activeElement) {
+ const chain: string[] = [];
+ let el = document.activeElement;
+
+ while (el) {
+ const tagName = el.tagName;
+ const id = el.id ? `#${el.id}` : '';
+ const classes = coalesce(el.className.split(/\s+/g).map(c => c.trim())).map(c => `.${c}`).join('');
+ chain.unshift(`${tagName}${id}${classes}`);
+
+ el = el.parentElement;
+ }
+
+ throw new Error(`Active element not found. Current active element is '${chain.join(' > ')}'. Looking for ${selector}`);
+ }
+
+ return true;
+ }
+
+ async getElements(selector: string, recursive: boolean): Promise {
+ const query = document.querySelectorAll(selector);
+ const result: IElement[] = [];
+
+ for (let i = 0; i < query.length; i++) {
+ const element = query.item(i);
+ result.push(serializeElement(element, recursive));
+ }
+
+ return result;
+ }
+
+ async getElementXY(selector: string, xoffset?: number, yoffset?: number): Promise<{ x: number; y: number; }> {
+ const offset = typeof xoffset === 'number' && typeof yoffset === 'number' ? { x: xoffset, y: yoffset } : undefined;
+ return this._getElementXY(selector, offset);
+ }
+
+ async typeInEditor(selector: string, text: string): Promise {
+ const element = document.querySelector(selector);
+
+ if (!element) {
+ throw new Error(`Editor not found: ${selector}`);
+ }
+
+ const textarea = element as HTMLTextAreaElement;
+ const start = textarea.selectionStart;
+ const newStart = start + text.length;
+ const value = textarea.value;
+ const newValue = value.substr(0, start) + text + value.substr(start);
+
+ textarea.value = newValue;
+ textarea.setSelectionRange(newStart, newStart);
+
+ const event = new Event('input', { 'bubbles': true, 'cancelable': true });
+ textarea.dispatchEvent(event);
+ }
+
+ async getTerminalBuffer(selector: string): Promise {
+ const element = document.querySelector(selector);
+
+ if (!element) {
+ throw new Error(`Terminal not found: ${selector}`);
+ }
+
+ const xterm = (element as any).xterm;
+
+ if (!xterm) {
+ throw new Error(`Xterm not found: ${selector}`);
+ }
+
+ const lines: string[] = [];
+
+ for (let i = 0; i < xterm.buffer.length; i++) {
+ lines.push(xterm.buffer.getLine(i)!.translateToString(true));
+ }
+
+ return lines;
+ }
+
+ async writeInTerminal(selector: string, text: string): Promise {
+ const element = document.querySelector(selector);
+
+ if (!element) {
+ throw new Error(`Element not found: ${selector}`);
+ }
+
+ const xterm = (element as any).xterm;
+
+ if (!xterm) {
+ throw new Error(`Xterm not found: ${selector}`);
+ }
+
+ xterm._core._coreService.triggerDataEvent(text);
+ }
+
+ protected async _getElementXY(selector: string, offset?: { x: number, y: number }): Promise<{ x: number; y: number; }> {
+ const element = document.querySelector(selector);
+
+ if (!element) {
+ return Promise.reject(new Error(`Element not found: ${selector}`));
+ }
+
+ const { left, top } = getTopLeftOffset(element as HTMLElement);
+ const { width, height } = getClientArea(element as HTMLElement);
+ let x: number, y: number;
+
+ if (offset) {
+ x = left + offset.x;
+ y = top + offset.y;
+ } else {
+ x = left + (width / 2);
+ y = top + (height / 2);
+ }
+
+ x = Math.round(x);
+ y = Math.round(y);
+
+ return { x, y };
+ }
+
+ abstract async openDevTools(): Promise;
+}
diff --git a/src/vs/platform/driver/browser/driver.ts b/src/vs/platform/driver/browser/driver.ts
new file mode 100644
index 0000000000..613655393e
--- /dev/null
+++ b/src/vs/platform/driver/browser/driver.ts
@@ -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 { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
+import { BaseWindowDriver } from 'vs/platform/driver/browser/baseDriver';
+
+class BrowserWindowDriver extends BaseWindowDriver {
+ click(selector: string, xoffset?: number | undefined, yoffset?: number | undefined): Promise {
+ throw new Error('Method not implemented.');
+ }
+ doubleClick(selector: string): Promise {
+ throw new Error('Method not implemented.');
+ }
+ openDevTools(): Promise {
+ throw new Error('Method not implemented.');
+ }
+}
+
+export async function registerWindowDriver(): Promise {
+ (window).driver = new BrowserWindowDriver();
+
+ return toDisposable(() => {
+ return { dispose: () => { } };
+ });
+}
diff --git a/src/vs/platform/driver/common/driver.ts b/src/vs/platform/driver/common/driver.ts
new file mode 100644
index 0000000000..694e5bf37d
--- /dev/null
+++ b/src/vs/platform/driver/common/driver.ts
@@ -0,0 +1,56 @@
+/*---------------------------------------------------------------------------------------------
+ * 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';
+
+// !! Do not remove the following START and END markers, they are parsed by the smoketest build
+
+//*START
+export interface IElement {
+ tagName: string;
+ className: string;
+ textContent: string;
+ attributes: { [name: string]: string; };
+ children: IElement[];
+ top: number;
+ left: number;
+}
+
+export interface IDriver {
+ _serviceBrand: any;
+
+ getWindowIds(): Promise;
+ capturePage(windowId: number): Promise;
+ reloadWindow(windowId: number): Promise;
+ exitApplication(): Promise;
+ dispatchKeybinding(windowId: number, keybinding: string): Promise;
+ click(windowId: number, selector: string, xoffset?: number | undefined, yoffset?: number | undefined): Promise;
+ doubleClick(windowId: number, selector: string): Promise;
+ setValue(windowId: number, selector: string, text: string): Promise;
+ getTitle(windowId: number): Promise;
+ isActiveElement(windowId: number, selector: string): Promise;
+ getElements(windowId: number, selector: string, recursive?: boolean): Promise;
+ getElementXY(windowId: number, selector: string, xoffset?: number, yoffset?: number): Promise<{ x: number; y: number; }>;
+ typeInEditor(windowId: number, selector: string, text: string): Promise;
+ getTerminalBuffer(windowId: number, selector: string): Promise;
+ writeInTerminal(windowId: number, selector: string, text: string): Promise;
+}
+//*END
+
+export const ID = 'driverService';
+export const IDriver = createDecorator(ID);
+
+export interface IWindowDriver {
+ click(selector: string, xoffset?: number | undefined, yoffset?: number | undefined): Promise;
+ doubleClick(selector: string): Promise;
+ setValue(selector: string, text: string): Promise;
+ getTitle(): Promise;
+ isActiveElement(selector: string): Promise;
+ getElements(selector: string, recursive: boolean): Promise;
+ getElementXY(selector: string, xoffset?: number, yoffset?: number): Promise<{ x: number; y: number; }>;
+ typeInEditor(selector: string, text: string): Promise;
+ getTerminalBuffer(selector: string): Promise;
+ writeInTerminal(selector: string, text: string): Promise;
+}
diff --git a/src/vs/platform/driver/electron-browser/driver.ts b/src/vs/platform/driver/electron-browser/driver.ts
index 920a1d19c8..262692fef9 100644
--- a/src/vs/platform/driver/electron-browser/driver.ts
+++ b/src/vs/platform/driver/electron-browser/driver.ts
@@ -4,55 +4,21 @@
*--------------------------------------------------------------------------------------------*/
import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
-import { IWindowDriver, IElement, WindowDriverChannel, WindowDriverRegistryChannelClient } from 'vs/platform/driver/node/driver';
+import { WindowDriverChannel, WindowDriverRegistryChannelClient } from 'vs/platform/driver/node/driver';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService';
-import { getTopLeftOffset, getClientArea } from 'vs/base/browser/dom';
import * as electron from 'electron';
import { IWindowService } from 'vs/platform/windows/common/windows';
-import { Terminal } from 'xterm';
import { timeout } from 'vs/base/common/async';
-import { coalesce } from 'vs/base/common/arrays';
+import { BaseWindowDriver } from 'vs/platform/driver/browser/baseDriver';
-function serializeElement(element: Element, recursive: boolean): IElement {
- const attributes = Object.create(null);
-
- for (let j = 0; j < element.attributes.length; j++) {
- const attr = element.attributes.item(j);
- if (attr) {
- attributes[attr.name] = attr.value;
- }
- }
-
- const children: IElement[] = [];
-
- if (recursive) {
- for (let i = 0; i < element.children.length; i++) {
- const child = element.children.item(i);
- if (child) {
- children.push(serializeElement(child, true));
- }
- }
- }
-
- const { left, top } = getTopLeftOffset(element as HTMLElement);
-
- return {
- tagName: element.tagName,
- className: element.className,
- textContent: element.textContent || '',
- attributes,
- children,
- left,
- top
- };
-}
-
-class WindowDriver implements IWindowDriver {
+class WindowDriver extends BaseWindowDriver {
constructor(
@IWindowService private readonly windowService: IWindowService
- ) { }
+ ) {
+ super();
+ }
click(selector: string, xoffset?: number, yoffset?: number): Promise {
const offset = typeof xoffset === 'number' && typeof yoffset === 'number' ? { x: xoffset, y: yoffset } : undefined;
@@ -63,31 +29,6 @@ class WindowDriver implements IWindowDriver {
return this._click(selector, 2);
}
- private async _getElementXY(selector: string, offset?: { x: number, y: number }): Promise<{ x: number; y: number; }> {
- const element = document.querySelector(selector);
-
- if (!element) {
- return Promise.reject(new Error(`Element not found: ${selector}`));
- }
-
- const { left, top } = getTopLeftOffset(element as HTMLElement);
- const { width, height } = getClientArea(element as HTMLElement);
- let x: number, y: number;
-
- if (offset) {
- x = left + offset.x;
- y = top + offset.y;
- } else {
- x = left + (width / 2);
- y = top + (height / 2);
- }
-
- x = Math.round(x);
- y = Math.round(y);
-
- return { x, y };
- }
-
private async _click(selector: string, clickCount: number, offset?: { x: number, y: number }): Promise {
const { x, y } = await this._getElementXY(selector, offset);
@@ -99,116 +40,6 @@ class WindowDriver implements IWindowDriver {
await timeout(100);
}
- async setValue(selector: string, text: string): Promise {
- const element = document.querySelector(selector);
-
- if (!element) {
- return Promise.reject(new Error(`Element not found: ${selector}`));
- }
-
- const inputElement = element as HTMLInputElement;
- inputElement.value = text;
-
- const event = new Event('input', { bubbles: true, cancelable: true });
- inputElement.dispatchEvent(event);
- }
-
- async getTitle(): Promise {
- return document.title;
- }
-
- async isActiveElement(selector: string): Promise {
- const element = document.querySelector(selector);
-
- if (element !== document.activeElement) {
- const chain: string[] = [];
- let el = document.activeElement;
-
- while (el) {
- const tagName = el.tagName;
- const id = el.id ? `#${el.id}` : '';
- const classes = coalesce(el.className.split(/\s+/g).map(c => c.trim())).map(c => `.${c}`).join('');
- chain.unshift(`${tagName}${id}${classes}`);
-
- el = el.parentElement;
- }
-
- throw new Error(`Active element not found. Current active element is '${chain.join(' > ')}'. Looking for ${selector}`);
- }
-
- return true;
- }
-
- async getElements(selector: string, recursive: boolean): Promise {
- const query = document.querySelectorAll(selector);
- const result: IElement[] = [];
-
- for (let i = 0; i < query.length; i++) {
- const element = query.item(i);
- result.push(serializeElement(element, recursive));
- }
-
- return result;
- }
-
- async typeInEditor(selector: string, text: string): Promise {
- const element = document.querySelector(selector);
-
- if (!element) {
- throw new Error(`Editor not found: ${selector}`);
- }
-
- const textarea = element as HTMLTextAreaElement;
- const start = textarea.selectionStart;
- const newStart = start + text.length;
- const value = textarea.value;
- const newValue = value.substr(0, start) + text + value.substr(start);
-
- textarea.value = newValue;
- textarea.setSelectionRange(newStart, newStart);
-
- const event = new Event('input', { 'bubbles': true, 'cancelable': true });
- textarea.dispatchEvent(event);
- }
-
- async getTerminalBuffer(selector: string): Promise {
- const element = document.querySelector(selector);
-
- if (!element) {
- throw new Error(`Terminal not found: ${selector}`);
- }
-
- const xterm: Terminal = (element as any).xterm;
-
- if (!xterm) {
- throw new Error(`Xterm not found: ${selector}`);
- }
-
- const lines: string[] = [];
-
- for (let i = 0; i < xterm.buffer.length; i++) {
- lines.push(xterm.buffer.getLine(i)!.translateToString(true));
- }
-
- return lines;
- }
-
- async writeInTerminal(selector: string, text: string): Promise {
- const element = document.querySelector(selector);
-
- if (!element) {
- throw new Error(`Element not found: ${selector}`);
- }
-
- const xterm: Terminal = (element as any).xterm;
-
- if (!xterm) {
- throw new Error(`Xterm not found: ${selector}`);
- }
-
- xterm._core._coreService.triggerDataEvent(text);
- }
-
async openDevTools(): Promise {
await this.windowService.openDevTools({ mode: 'detach' });
}
diff --git a/src/vs/platform/driver/electron-main/driver.ts b/src/vs/platform/driver/electron-main/driver.ts
index 90e93eae7e..566830d3a1 100644
--- a/src/vs/platform/driver/electron-main/driver.ts
+++ b/src/vs/platform/driver/electron-main/driver.ts
@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import { IDriver, DriverChannel, IElement, WindowDriverChannelClient, IWindowDriverRegistry, WindowDriverRegistryChannel, IWindowDriver, IDriverOptions } from 'vs/platform/driver/node/driver';
+import { DriverChannel, WindowDriverChannelClient, IWindowDriverRegistry, WindowDriverRegistryChannel, IDriverOptions } from 'vs/platform/driver/node/driver';
import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows';
import { serve as serveNet } from 'vs/base/parts/ipc/node/ipc.net';
import { combinedDisposable, IDisposable } from 'vs/base/common/lifecycle';
@@ -17,6 +17,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment'
import { ScanCodeBinding } from 'vs/base/common/scanCode';
import { KeybindingParser } from 'vs/base/common/keybindingParser';
import { timeout } from 'vs/base/common/async';
+import { IDriver, IElement, IWindowDriver } from 'vs/platform/driver/common/driver';
function isSilentKeyCode(keyCode: KeyCode) {
return keyCode < KeyCode.KEY_0;
@@ -163,6 +164,11 @@ export class Driver implements IDriver, IWindowDriverRegistry {
return await windowDriver.getElements(selector, recursive);
}
+ async getElementXY(windowId: number, selector: string, xoffset?: number, yoffset?: number): Promise<{ x: number; y: number; }> {
+ const windowDriver = await this.getWindowDriver(windowId);
+ return await windowDriver.getElementXY(selector, xoffset, yoffset);
+ }
+
async typeInEditor(windowId: number, selector: string, text: string): Promise {
const windowDriver = await this.getWindowDriver(windowId);
await windowDriver.typeInEditor(selector, text);
diff --git a/src/vs/platform/driver/node/driver.ts b/src/vs/platform/driver/node/driver.ts
index f3efafb598..2cf10644b4 100644
--- a/src/vs/platform/driver/node/driver.ts
+++ b/src/vs/platform/driver/node/driver.ts
@@ -5,45 +5,9 @@
import { Client } from 'vs/base/parts/ipc/common/ipc.net';
import { connect as connectNet } from 'vs/base/parts/ipc/node/ipc.net';
-import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc';
import { Event } from 'vs/base/common/event';
-
-export const ID = 'driverService';
-export const IDriver = createDecorator(ID);
-
-// !! Do not remove the following START and END markers, they are parsed by the smoketest build
-
-//*START
-export interface IElement {
- tagName: string;
- className: string;
- textContent: string;
- attributes: { [name: string]: string; };
- children: IElement[];
- top: number;
- left: number;
-}
-
-export interface IDriver {
- _serviceBrand: any;
-
- getWindowIds(): Promise;
- capturePage(windowId: number): Promise;
- reloadWindow(windowId: number): Promise;
- exitApplication(): Promise;
- dispatchKeybinding(windowId: number, keybinding: string): Promise;
- click(windowId: number, selector: string, xoffset?: number | undefined, yoffset?: number | undefined): Promise;
- doubleClick(windowId: number, selector: string): Promise;
- setValue(windowId: number, selector: string, text: string): Promise;
- getTitle(windowId: number): Promise;
- isActiveElement(windowId: number, selector: string): Promise;
- getElements(windowId: number, selector: string, recursive?: boolean): Promise;
- typeInEditor(windowId: number, selector: string, text: string): Promise;
- getTerminalBuffer(windowId: number, selector: string): Promise;
- writeInTerminal(windowId: number, selector: string, text: string): Promise;
-}
-//*END
+import { IDriver, IElement, IWindowDriver } from 'vs/platform/driver/common/driver';
export class DriverChannel implements IServerChannel {
@@ -66,6 +30,7 @@ export class DriverChannel implements IServerChannel {
case 'getTitle': return this.driver.getTitle(arg[0]);
case 'isActiveElement': return this.driver.isActiveElement(arg[0], arg[1]);
case 'getElements': return this.driver.getElements(arg[0], arg[1], arg[2]);
+ case 'getElementXY': return this.driver.getElementXY(arg[0], arg[1], arg[2]);
case 'typeInEditor': return this.driver.typeInEditor(arg[0], arg[1], arg[2]);
case 'getTerminalBuffer': return this.driver.getTerminalBuffer(arg[0], arg[1]);
case 'writeInTerminal': return this.driver.writeInTerminal(arg[0], arg[1], arg[2]);
@@ -125,6 +90,10 @@ export class DriverChannelClient implements IDriver {
return this.channel.call('getElements', [windowId, selector, recursive]);
}
+ getElementXY(windowId: number, selector: string, xoffset: number | undefined, yoffset: number | undefined): Promise<{ x: number, y: number }> {
+ return this.channel.call('getElementXY', [windowId, selector, xoffset, yoffset]);
+ }
+
typeInEditor(windowId: number, selector: string, text: string): Promise {
return this.channel.call('typeInEditor', [windowId, selector, text]);
}
@@ -180,18 +149,6 @@ export class WindowDriverRegistryChannelClient implements IWindowDriverRegistry
}
}
-export interface IWindowDriver {
- click(selector: string, xoffset?: number | undefined, yoffset?: number | undefined): Promise;
- doubleClick(selector: string): Promise;
- setValue(selector: string, text: string): Promise;
- getTitle(): Promise;
- isActiveElement(selector: string): Promise;
- getElements(selector: string, recursive: boolean): Promise;
- typeInEditor(selector: string, text: string): Promise;
- getTerminalBuffer(selector: string): Promise;
- writeInTerminal(selector: string, text: string): Promise;
-}
-
export class WindowDriverChannel implements IServerChannel {
constructor(private driver: IWindowDriver) { }
@@ -208,6 +165,7 @@ export class WindowDriverChannel implements IServerChannel {
case 'getTitle': return this.driver.getTitle();
case 'isActiveElement': return this.driver.isActiveElement(arg);
case 'getElements': return this.driver.getElements(arg[0], arg[1]);
+ case 'getElementXY': return this.driver.getElementXY(arg[0], arg[1], arg[2]);
case 'typeInEditor': return this.driver.typeInEditor(arg[0], arg[1]);
case 'getTerminalBuffer': return this.driver.getTerminalBuffer(arg);
case 'writeInTerminal': return this.driver.writeInTerminal(arg[0], arg[1]);
@@ -247,6 +205,10 @@ export class WindowDriverChannelClient implements IWindowDriver {
return this.channel.call('getElements', [selector, recursive]);
}
+ getElementXY(selector: string, xoffset?: number, yoffset?: number): Promise<{ x: number, y: number }> {
+ return this.channel.call('getElementXY', [selector, xoffset, yoffset]);
+ }
+
typeInEditor(selector: string, text: string): Promise {
return this.channel.call('typeInEditor', [selector, text]);
}
diff --git a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts
index c5d4278be3..fa0cc37363 100644
--- a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts
+++ b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts
@@ -392,10 +392,10 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
@IProductService private readonly productService: IProductService,
@optional(IStorageService) private readonly storageService: IStorageService,
) {
- const config = productService.productConfiguration.extensionsGallery;
+ const config = productService.extensionsGallery;
this.extensionsGalleryUrl = config && config.serviceUrl;
this.extensionsControlUrl = config && config.controlUrl;
- this.commonHeadersPromise = resolveMarketplaceHeaders(productService.productConfiguration.version, this.environmentService, this.fileService, this.storageService);
+ this.commonHeadersPromise = resolveMarketplaceHeaders(productService.version, this.environmentService, this.fileService, this.storageService);
}
private api(path = ''): string {
@@ -440,7 +440,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
const versionAsset = rawExtension.versions.filter(v => v.version === version)[0];
if (versionAsset) {
const extension = toExtension(rawExtension, versionAsset, 0, query);
- if (extension.properties.engine && isEngineValid(extension.properties.engine, this.productService.productConfiguration.version)) {
+ if (extension.properties.engine && isEngineValid(extension.properties.engine, this.productService.version)) {
return extension;
}
}
@@ -788,7 +788,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
return this.queryGallery(query, CancellationToken.None).then(({ galleryExtensions }) => {
if (galleryExtensions.length) {
if (compatible) {
- return Promise.all(galleryExtensions[0].versions.map(v => this.getEngine(v).then(engine => isEngineValid(engine, this.productService.productConfiguration.version) ? v : null)))
+ return Promise.all(galleryExtensions[0].versions.map(v => this.getEngine(v).then(engine => isEngineValid(engine, this.productService.version) ? v : null)))
.then(versions => versions
.filter(v => !!v)
.map(v => ({ version: v!.version, date: v!.lastUpdated })));
@@ -874,7 +874,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
if (!engine) {
return null;
}
- if (isEngineValid(engine, this.productService.productConfiguration.version)) {
+ if (isEngineValid(engine, this.productService.version)) {
return Promise.resolve(version);
}
}
@@ -906,7 +906,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
const version = versions[0];
return this.getEngine(version)
.then(engine => {
- if (!isEngineValid(engine, this.productService.productConfiguration.version)) {
+ if (!isEngineValid(engine, this.productService.version)) {
return this.getLastValidExtensionVersionRecursively(extension, versions.slice(1));
}
diff --git a/src/vs/platform/files/common/files.ts b/src/vs/platform/files/common/files.ts
index da3abd0630..85d33b60ed 100644
--- a/src/vs/platform/files/common/files.ts
+++ b/src/vs/platform/files/common/files.ts
@@ -620,26 +620,26 @@ export interface IReadFileOptions {
* that have been read already with the same etag.
* It is the task of the caller to makes sure to handle this error case from the promise.
*/
- etag?: string;
+ readonly etag?: string;
/**
* Is an integer specifying where to begin reading from in the file. If position is null,
* data will be read from the current file position.
*/
- position?: number;
+ readonly position?: number;
/**
* Is an integer specifying how many bytes to read from the file. By default, all bytes
* will be read.
*/
- length?: number;
+ readonly length?: number;
/**
* If provided, the size of the file will be checked against the limits.
*/
limits?: {
- size?: number;
- memory?: number;
+ readonly size?: number;
+ readonly memory?: number;
};
}
@@ -648,12 +648,12 @@ export interface IWriteFileOptions {
/**
* The last known modification time of the file. This can be used to prevent dirty writes.
*/
- mtime?: number;
+ readonly mtime?: number;
/**
* The etag of the file. This can be used to prevent dirty writes.
*/
- etag?: string;
+ readonly etag?: string;
}
export interface IResolveFileOptions {
@@ -662,22 +662,22 @@ export interface IResolveFileOptions {
* Automatically continue resolving children of a directory until the provided resources
* are found.
*/
- resolveTo?: URI[];
+ readonly resolveTo?: readonly URI[];
/**
* Automatically continue resolving children of a directory if the number of children is 1.
*/
- resolveSingleChildDescendants?: boolean;
+ readonly resolveSingleChildDescendants?: boolean;
/**
* Will resolve mtime, size and etag of files if enabled. This can have a negative impact
* on performance and thus should only be used when these values are required.
*/
- resolveMetadata?: boolean;
+ readonly resolveMetadata?: boolean;
}
export interface IResolveMetadataFileOptions extends IResolveFileOptions {
- resolveMetadata: true;
+ readonly resolveMetadata: true;
}
export interface ICreateFileOptions {
@@ -686,7 +686,7 @@ export interface ICreateFileOptions {
* Overwrite the file to create if it already exists on disk. Otherwise
* an error will be thrown (FILE_MODIFIED_SINCE).
*/
- overwrite?: boolean;
+ readonly overwrite?: boolean;
}
export class FileOperationError extends Error {
diff --git a/src/vs/platform/files/node/watcher/win32/csharpWatcherService.ts b/src/vs/platform/files/node/watcher/win32/csharpWatcherService.ts
index 075c770507..77d4251922 100644
--- a/src/vs/platform/files/node/watcher/win32/csharpWatcherService.ts
+++ b/src/vs/platform/files/node/watcher/win32/csharpWatcherService.ts
@@ -134,7 +134,7 @@ export class OutOfProcessWin32FolderWatcher {
public dispose(): void {
if (this.handle) {
this.handle.kill();
- this.handle = null!; // StrictNullOverride: nulling out ok in dispose
+ this.handle = undefined;
}
}
}
diff --git a/src/vs/platform/issue/electron-browser/issueService.ts b/src/vs/platform/issue/electron-browser/issueService.ts
index 109ac4335d..f709d07cfd 100644
--- a/src/vs/platform/issue/electron-browser/issueService.ts
+++ b/src/vs/platform/issue/electron-browser/issueService.ts
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { IChannel } from 'vs/base/parts/ipc/common/ipc';
-import { IIssueService, IssueReporterData, ProcessExplorerData } from 'vs/platform/issue/common/issue';
+import { IIssueService, IssueReporterData, ProcessExplorerData } from 'vs/platform/issue/node/issue';
import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService';
import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
diff --git a/src/vs/platform/issue/electron-main/issueService.ts b/src/vs/platform/issue/electron-main/issueService.ts
index c98bad3432..49dd08b60b 100644
--- a/src/vs/platform/issue/electron-main/issueService.ts
+++ b/src/vs/platform/issue/electron-main/issueService.ts
@@ -6,10 +6,11 @@
import { localize } from 'vs/nls';
import * as objects from 'vs/base/common/objects';
import { parseArgs } from 'vs/platform/environment/node/argv';
-import { IIssueService, IssueReporterData, IssueReporterFeatures, ProcessExplorerData } from 'vs/platform/issue/common/issue';
+import { IIssueService, IssueReporterData, IssueReporterFeatures, ProcessExplorerData } from 'vs/platform/issue/node/issue';
import { BrowserWindow, ipcMain, screen, Event, dialog } from 'electron';
import { ILaunchService } from 'vs/platform/launch/electron-main/launchService';
-import { PerformanceInfo, IDiagnosticsService, isRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnosticsService';
+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';
diff --git a/src/vs/platform/issue/common/issue.ts b/src/vs/platform/issue/node/issue.ts
similarity index 100%
rename from src/vs/platform/issue/common/issue.ts
rename to src/vs/platform/issue/node/issue.ts
diff --git a/src/vs/platform/issue/node/issueIpc.ts b/src/vs/platform/issue/node/issueIpc.ts
index 7f55676c33..ae64e84335 100644
--- a/src/vs/platform/issue/node/issueIpc.ts
+++ b/src/vs/platform/issue/node/issueIpc.ts
@@ -5,7 +5,7 @@
import { IServerChannel } from 'vs/base/parts/ipc/common/ipc';
import { Event } from 'vs/base/common/event';
-import { IIssueService } from 'vs/platform/issue/common/issue';
+import { IIssueService } from 'vs/platform/issue/node/issue';
export class IssueChannel implements IServerChannel {
diff --git a/src/vs/platform/launch/electron-main/launchService.ts b/src/vs/platform/launch/electron-main/launchService.ts
index 3fdf0812be..33be9edb5e 100644
--- a/src/vs/platform/launch/electron-main/launchService.ts
+++ b/src/vs/platform/launch/electron-main/launchService.ts
@@ -19,7 +19,7 @@ import { BrowserWindow, ipcMain, Event as IpcEvent, app } from 'electron';
import { Event } from 'vs/base/common/event';
import { hasArgs } from 'vs/platform/environment/node/argv';
import { coalesce } from 'vs/base/common/arrays';
-import { IDiagnosticInfoOptions, IDiagnosticInfo, IRemoteDiagnosticInfo, IRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnosticsService';
+import { IDiagnosticInfoOptions, IDiagnosticInfo, IRemoteDiagnosticInfo, IRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnostics';
import { IMainProcessInfo, IWindowInfo } from 'vs/platform/launch/common/launchService';
export const ID = 'launchService';
diff --git a/src/vs/platform/product/browser/productService.ts b/src/vs/platform/product/browser/productService.ts
deleted file mode 100644
index fd2cf7d966..0000000000
--- a/src/vs/platform/product/browser/productService.ts
+++ /dev/null
@@ -1,27 +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 { IProductService, IProductConfiguration } from 'vs/platform/product/common/product';
-import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
-
-export class ProductService implements IProductService {
-
- _serviceBrand!: ServiceIdentifier;
-
- readonly productConfiguration: IProductConfiguration;
-
- constructor() {
- const element = document.getElementById('vscode-remote-product-configuration');
- this.productConfiguration = {
- ...element ? JSON.parse(element.getAttribute('data-settings')!) : {
- version: '1.38.0-unknown',
- nameLong: 'Unknown',
- extensionAllowedProposedApi: [],
- }, ...{ urlProtocol: '', enableTelemetry: false },
- ...{ vscodeVersion: '1.35.0' } // {{SQL CARBON EDIT}} add vscodeversion
- };
- }
-
-}
diff --git a/src/vs/platform/product/common/product.ts b/src/vs/platform/product/common/product.ts
index d8eaf3de15..2a75ea306e 100644
--- a/src/vs/platform/product/common/product.ts
+++ b/src/vs/platform/product/common/product.ts
@@ -3,19 +3,18 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
+import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
export const IProductService = createDecorator('productService');
-export interface IProductService {
+export interface IProductService extends Readonly {
- _serviceBrand: ServiceIdentifier;
+ _serviceBrand: undefined;
- readonly productConfiguration: IProductConfiguration;
}
export interface IProductConfiguration {
- readonly version: string;
+ version: string;
nameShort: string;
nameLong: string;
readonly applicationName: string;
@@ -51,7 +50,6 @@ export interface IProductConfiguration {
readonly extensionImportantTips: { [id: string]: { name: string; pattern: string; isExtensionPack?: boolean }; };
readonly exeBasedExtensionTips: { [id: string]: IExeBasedExtensionTip; };
readonly extensionKeywords: { [extension: string]: readonly string[]; };
- readonly extensionAllowedBadgeProviders: readonly string[];
readonly extensionAllowedProposedApi: readonly string[];
readonly keymapExtensionTips: readonly string[];
readonly crashReporter: {
diff --git a/src/vs/platform/product/node/product.ts b/src/vs/platform/product/node/product.ts
index e53e6a8f65..51de6b2f06 100644
--- a/src/vs/platform/product/node/product.ts
+++ b/src/vs/platform/product/node/product.ts
@@ -6,6 +6,7 @@
import * as path from 'vs/base/common/path';
import { getPathFromAmdModule } from 'vs/base/common/amd';
import { IProductConfiguration } from 'vs/platform/product/common/product';
+import pkg from 'vs/platform/product/node/package';
const rootPath = path.dirname(getPathFromAmdModule(require, ''));
const productJsonPath = path.join(rootPath, 'product.json');
@@ -17,4 +18,6 @@ if (process.env['VSCODE_DEV']) {
product.dataFolderName += '-dev';
}
+product.version = pkg.version;
+
export default product;
diff --git a/src/vs/platform/product/node/productService.ts b/src/vs/platform/product/node/productService.ts
deleted file mode 100644
index 9721daf86c..0000000000
--- a/src/vs/platform/product/node/productService.ts
+++ /dev/null
@@ -1,23 +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 { IProductService, IProductConfiguration } from 'vs/platform/product/common/product';
-import product from 'vs/platform/product/node/product';
-import pkg from 'vs/platform/product/node/package';
-import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
-
-export class ProductService implements IProductService {
-
- _serviceBrand!: ServiceIdentifier;
-
- readonly productConfiguration: IProductConfiguration;
-
- constructor() {
- this.productConfiguration = {
- ...product, ...{ version: pkg.version }, ...{ vscodeVersion: '1.35.0' } // {{SQL CARBON EDIT}} add vscodeversion}
- };
- }
-
-}
diff --git a/src/vs/platform/remote/common/remoteAgentEnvironment.ts b/src/vs/platform/remote/common/remoteAgentEnvironment.ts
index e493a5f0c7..1d2cadba27 100644
--- a/src/vs/platform/remote/common/remoteAgentEnvironment.ts
+++ b/src/vs/platform/remote/common/remoteAgentEnvironment.ts
@@ -9,6 +9,7 @@ import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'
export interface IRemoteAgentEnvironment {
pid: number;
+ connectionToken: string;
appRoot: URI;
appSettingsHome: URI;
settingsPath: URI;
diff --git a/src/vs/platform/remote/electron-browser/remoteAuthorityResolverService.ts b/src/vs/platform/remote/electron-browser/remoteAuthorityResolverService.ts
index 1f648271d8..b92262cef5 100644
--- a/src/vs/platform/remote/electron-browser/remoteAuthorityResolverService.ts
+++ b/src/vs/platform/remote/electron-browser/remoteAuthorityResolverService.ts
@@ -6,6 +6,7 @@
import { ResolvedAuthority, IRemoteAuthorityResolverService, ResolverResult, ResolvedOptions } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { ipcRenderer as ipc } from 'electron';
import * as errors from 'vs/base/common/errors';
+import { RemoteAuthorities } from 'vs/base/common/network';
class PendingResolveAuthorityRequest {
constructor(
@@ -50,6 +51,7 @@ export class RemoteAuthorityResolverService implements IRemoteAuthorityResolverS
if (this._resolveAuthorityRequests[resolvedAuthority.authority]) {
let request = this._resolveAuthorityRequests[resolvedAuthority.authority];
ipc.send('vscode:remoteAuthorityResolved', resolvedAuthority);
+ RemoteAuthorities.set(resolvedAuthority.authority, resolvedAuthority.host, resolvedAuthority.port);
request.resolve({ authority: resolvedAuthority, options });
}
}
diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts
index 2420981064..2177348b39 100644
--- a/src/vs/platform/windows/common/windows.ts
+++ b/src/vs/platform/windows/common/windows.ts
@@ -96,12 +96,12 @@ export interface IWindowsService {
_serviceBrand: any;
- onWindowOpen: Event;
- onWindowFocus: Event;
- onWindowBlur: Event;
- onWindowMaximize: Event;
- onWindowUnmaximize: Event;
- onRecentlyOpenedChange: Event;
+ readonly onWindowOpen: Event;
+ readonly onWindowFocus: Event;
+ readonly onWindowBlur: Event;
+ readonly onWindowMaximize: Event;
+ readonly onWindowUnmaximize: Event;
+ readonly onRecentlyOpenedChange: Event;
// Dialogs
pickFileFolderAndOpen(options: INativeOpenDialogOptions): Promise;
diff --git a/src/vs/platform/windows/common/windowsIpc.ts b/src/vs/platform/windows/common/windowsIpc.ts
index 41849216ba..2323c97e43 100644
--- a/src/vs/platform/windows/common/windowsIpc.ts
+++ b/src/vs/platform/windows/common/windowsIpc.ts
@@ -12,14 +12,14 @@ import { IRecent, isRecentFile, isRecentFolder } from 'vs/platform/history/commo
export class WindowsChannel implements IServerChannel {
- private onWindowOpen: Event;
- private onWindowFocus: Event;
- private onWindowBlur: Event;
- private onWindowMaximize: Event;
- private onWindowUnmaximize: Event;
- private onRecentlyOpenedChange: Event;
+ private readonly onWindowOpen: Event;
+ private readonly onWindowFocus: Event;
+ private readonly onWindowBlur: Event;
+ private readonly onWindowMaximize: Event;
+ private readonly onWindowUnmaximize: Event;
+ private readonly onRecentlyOpenedChange: Event;
- constructor(private service: IWindowsService) {
+ constructor(private readonly service: IWindowsService) {
this.onWindowOpen = Event.buffer(service.onWindowOpen, true);
this.onWindowFocus = Event.buffer(service.onWindowFocus, true);
this.onWindowBlur = Event.buffer(service.onWindowBlur, true);
@@ -120,4 +120,4 @@ export class WindowsChannel implements IServerChannel {
throw new Error(`Call not found: ${command}`);
}
-}
\ No newline at end of file
+}
diff --git a/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts b/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts
index 9f6facb91e..0502fedae5 100644
--- a/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts
+++ b/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts
@@ -17,6 +17,7 @@ import { URI } from 'vs/base/common/uri';
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';
suite('WorkspacesMainService', () => {
const parentDir = getRandomTestPath(os.tmpdir(), 'vsctests', 'workspacesservice');
@@ -51,13 +52,13 @@ suite('WorkspacesMainService', () => {
let service: TestWorkspacesMainService;
- setup(() => {
+ setup(async () => {
service = new TestWorkspacesMainService(environmentService, logService);
// Delete any existing backups completely and then re-create it.
- return pfs.rimraf(untitledWorkspacesHomePath, pfs.RimRafMode.MOVE).then(() => {
- return pfs.mkdirp(untitledWorkspacesHomePath);
- });
+ await pfs.rimraf(untitledWorkspacesHomePath, pfs.RimRafMode.MOVE);
+
+ return pfs.mkdirp(untitledWorkspacesHomePath);
});
teardown(() => {
@@ -77,57 +78,50 @@ suite('WorkspacesMainService', () => {
assert.equal(u1.toString(), u2.toString());
}
- test('createWorkspace (folders)', () => {
- return createWorkspace([process.cwd(), os.tmpdir()]).then(workspace => {
- assert.ok(workspace);
- assert.ok(fs.existsSync(workspace.configPath.fsPath));
- assert.ok(service.isUntitledWorkspace(workspace));
+ test('createWorkspace (folders)', async () => {
+ const workspace = await createWorkspace([process.cwd(), os.tmpdir()]);
+ assert.ok(workspace);
+ assert.ok(fs.existsSync(workspace.configPath.fsPath));
+ assert.ok(service.isUntitledWorkspace(workspace));
- const ws = JSON.parse(fs.readFileSync(workspace.configPath.fsPath).toString()) as IStoredWorkspace;
- assert.equal(ws.folders.length, 2); //
- assertPathEquals((ws.folders[0]).path, process.cwd());
- assertPathEquals((ws.folders[1]).path, os.tmpdir());
-
- assert.ok(!(ws.folders[0]).name);
- assert.ok(!(ws.folders[1]).name);
- });
+ const ws = (JSON.parse(fs.readFileSync(workspace.configPath.fsPath).toString()) as IStoredWorkspace);
+ assert.equal(ws.folders.length, 2);
+ assertPathEquals((ws.folders[0]).path, process.cwd());
+ assertPathEquals((ws.folders[1]).path, os.tmpdir());
+ assert.ok(!(ws.folders[0]).name);
+ assert.ok(!(ws.folders[1]).name);
});
- test('createWorkspace (folders with name)', () => {
- return createWorkspace([process.cwd(), os.tmpdir()], ['currentworkingdirectory', 'tempdir']).then(workspace => {
- assert.ok(workspace);
- assert.ok(fs.existsSync(workspace.configPath.fsPath));
- assert.ok(service.isUntitledWorkspace(workspace));
+ test('createWorkspace (folders with name)', async () => {
+ const workspace = await createWorkspace([process.cwd(), os.tmpdir()], ['currentworkingdirectory', 'tempdir']);
+ assert.ok(workspace);
+ assert.ok(fs.existsSync(workspace.configPath.fsPath));
+ assert.ok(service.isUntitledWorkspace(workspace));
- const ws = JSON.parse(fs.readFileSync(workspace.configPath.fsPath).toString()) as IStoredWorkspace;
- assert.equal(ws.folders.length, 2); //
- assertPathEquals((ws.folders[0]).path, process.cwd());
- assertPathEquals((ws.folders[1]).path, os.tmpdir());
-
- assert.equal((ws.folders[0]).name, 'currentworkingdirectory');
- assert.equal((ws.folders[1]).name, 'tempdir');
- });
+ const ws = (JSON.parse(fs.readFileSync(workspace.configPath.fsPath).toString()) as IStoredWorkspace);
+ assert.equal(ws.folders.length, 2);
+ assertPathEquals((ws.folders[0]).path, process.cwd());
+ assertPathEquals((ws.folders[1]).path, os.tmpdir());
+ assert.equal((ws.folders[0]).name, 'currentworkingdirectory');
+ assert.equal((ws.folders[1]).name, 'tempdir');
});
- test('createUntitledWorkspace (folders as other resource URIs)', () => {
+ test('createUntitledWorkspace (folders as other resource URIs)', async () => {
const folder1URI = URI.parse('myscheme://server/work/p/f1');
const folder2URI = URI.parse('myscheme://server/work/o/f3');
- return service.createUntitledWorkspace([{ uri: folder1URI }, { uri: folder2URI }], 'server').then(workspace => {
- assert.ok(workspace);
- assert.ok(fs.existsSync(workspace.configPath.fsPath));
- assert.ok(service.isUntitledWorkspace(workspace));
+ const workspace = await service.createUntitledWorkspace([{ uri: folder1URI }, { uri: folder2URI }], 'server');
+ assert.ok(workspace);
+ assert.ok(fs.existsSync(workspace.configPath.fsPath));
+ assert.ok(service.isUntitledWorkspace(workspace));
- const ws = JSON.parse(fs.readFileSync(workspace.configPath.fsPath).toString()) as IStoredWorkspace;
- assert.equal(ws.folders.length, 2);
- assert.equal((ws.folders[0]).uri, folder1URI.toString(true));
- assert.equal((ws.folders[1]).uri, folder2URI.toString(true));
-
- assert.ok(!(ws.folders[0]).name);
- assert.ok(!(ws.folders[1]).name);
-
- assert.equal(ws.remoteAuthority, 'server');
- });
+ const ws = (JSON.parse(fs.readFileSync(workspace.configPath.fsPath).toString()) as IStoredWorkspace);
+ assert.equal(ws.folders.length, 2);
+ assert.equal((ws.folders[0]).uri, folder1URI.toString(true));
+ assert.equal((ws.folders[1]).uri, folder2URI.toString(true));
+ assert.ok(!(ws.folders[0]).name);
+ assert.ok(!(ws.folders[1]).name);
+ assert.equal(ws.remoteAuthority, 'server');
});
test('createWorkspaceSync (folders)', () => {
@@ -178,145 +172,130 @@ suite('WorkspacesMainService', () => {
assert.ok(!(ws.folders[1]).name);
});
- test('resolveWorkspaceSync', () => {
- return createWorkspace([process.cwd(), os.tmpdir()]).then(workspace => {
- assert.ok(service.resolveLocalWorkspaceSync(workspace.configPath));
+ test('resolveWorkspaceSync', async () => {
+ const workspace = await createWorkspace([process.cwd(), os.tmpdir()]);
+ assert.ok(service.resolveLocalWorkspaceSync(workspace.configPath));
- // make it a valid workspace path
- const newPath = path.join(path.dirname(workspace.configPath.fsPath), `workspace.${WORKSPACE_EXTENSION}`);
- fs.renameSync(workspace.configPath.fsPath, newPath);
- workspace.configPath = URI.file(newPath);
+ // make it a valid workspace path
+ const newPath = path.join(path.dirname(workspace.configPath.fsPath), `workspace.${WORKSPACE_EXTENSION}`);
+ fs.renameSync(workspace.configPath.fsPath, newPath);
+ workspace.configPath = URI.file(newPath);
- const resolved = service.resolveLocalWorkspaceSync(workspace.configPath);
- assert.equal(2, resolved!.folders.length);
- assertEqualURI(resolved!.configPath, workspace.configPath);
- assert.ok(resolved!.id);
+ const resolved = service.resolveLocalWorkspaceSync(workspace.configPath);
+ assert.equal(2, resolved!.folders.length);
+ assertEqualURI(resolved!.configPath, workspace.configPath);
+ assert.ok(resolved!.id);
+ fs.writeFileSync(workspace.configPath.fsPath, JSON.stringify({ something: 'something' })); // invalid workspace
- fs.writeFileSync(workspace.configPath.fsPath, JSON.stringify({ something: 'something' })); // invalid workspace
- const resolvedInvalid = service.resolveLocalWorkspaceSync(workspace.configPath);
- assert.ok(!resolvedInvalid);
- });
+ const resolvedInvalid = service.resolveLocalWorkspaceSync(workspace.configPath);
+ assert.ok(!resolvedInvalid);
});
- test('resolveWorkspaceSync (support relative paths)', () => {
- return createWorkspace([process.cwd(), os.tmpdir()]).then(workspace => {
- fs.writeFileSync(workspace.configPath.fsPath, JSON.stringify({ folders: [{ path: './ticino-playground/lib' }] }));
+ test('resolveWorkspaceSync (support relative paths)', async () => {
+ const workspace = await createWorkspace([process.cwd(), os.tmpdir()]);
+ fs.writeFileSync(workspace.configPath.fsPath, JSON.stringify({ folders: [{ path: './ticino-playground/lib' }] }));
- const resolved = service.resolveLocalWorkspaceSync(workspace.configPath);
- assertEqualURI(resolved!.folders[0].uri, URI.file(path.join(path.dirname(workspace.configPath.fsPath), 'ticino-playground', 'lib')));
- });
+ const resolved = service.resolveLocalWorkspaceSync(workspace.configPath);
+ assertEqualURI(resolved!.folders[0].uri, URI.file(path.join(path.dirname(workspace.configPath.fsPath), 'ticino-playground', 'lib')));
});
- test('resolveWorkspaceSync (support relative paths #2)', () => {
- return createWorkspace([process.cwd(), os.tmpdir()]).then(workspace => {
- fs.writeFileSync(workspace.configPath.fsPath, JSON.stringify({ folders: [{ path: './ticino-playground/lib/../other' }] }));
+ test('resolveWorkspaceSync (support relative paths #2)', async () => {
+ const workspace = await createWorkspace([process.cwd(), os.tmpdir()]);
+ fs.writeFileSync(workspace.configPath.fsPath, JSON.stringify({ folders: [{ path: './ticino-playground/lib/../other' }] }));
- const resolved = service.resolveLocalWorkspaceSync(workspace.configPath);
- assertEqualURI(resolved!.folders[0].uri, URI.file(path.join(path.dirname(workspace.configPath.fsPath), 'ticino-playground', 'other')));
- });
+ const resolved = service.resolveLocalWorkspaceSync(workspace.configPath);
+ assertEqualURI(resolved!.folders[0].uri, URI.file(path.join(path.dirname(workspace.configPath.fsPath), 'ticino-playground', 'other')));
});
- test('resolveWorkspaceSync (support relative paths #3)', () => {
- return createWorkspace([process.cwd(), os.tmpdir()]).then(workspace => {
- fs.writeFileSync(workspace.configPath.fsPath, JSON.stringify({ folders: [{ path: 'ticino-playground/lib' }] }));
+ test('resolveWorkspaceSync (support relative paths #3)', async () => {
+ const workspace = await createWorkspace([process.cwd(), os.tmpdir()]);
+ fs.writeFileSync(workspace.configPath.fsPath, JSON.stringify({ folders: [{ path: 'ticino-playground/lib' }] }));
- const resolved = service.resolveLocalWorkspaceSync(workspace.configPath);
- assertEqualURI(resolved!.folders[0].uri, URI.file(path.join(path.dirname(workspace.configPath.fsPath), 'ticino-playground', 'lib')));
- });
+ const resolved = service.resolveLocalWorkspaceSync(workspace.configPath);
+ assertEqualURI(resolved!.folders[0].uri, URI.file(path.join(path.dirname(workspace.configPath.fsPath), 'ticino-playground', 'lib')));
});
- test('resolveWorkspaceSync (support invalid JSON via fault tolerant parsing)', () => {
- return createWorkspace([process.cwd(), os.tmpdir()]).then(workspace => {
- fs.writeFileSync(workspace.configPath.fsPath, '{ "folders": [ { "path": "./ticino-playground/lib" } , ] }'); // trailing comma
+ test('resolveWorkspaceSync (support invalid JSON via fault tolerant parsing)', async () => {
+ const workspace = await createWorkspace([process.cwd(), os.tmpdir()]);
+ fs.writeFileSync(workspace.configPath.fsPath, '{ "folders": [ { "path": "./ticino-playground/lib" } , ] }'); // trailing comma
- const resolved = service.resolveLocalWorkspaceSync(workspace.configPath);
- assertEqualURI(resolved!.folders[0].uri, URI.file(path.join(path.dirname(workspace.configPath.fsPath), 'ticino-playground', 'lib')));
- });
+ const resolved = service.resolveLocalWorkspaceSync(workspace.configPath);
+ assertEqualURI(resolved!.folders[0].uri, URI.file(path.join(path.dirname(workspace.configPath.fsPath), 'ticino-playground', 'lib')));
});
- test('rewriteWorkspaceFileForNewLocation', () => {
+ test('rewriteWorkspaceFileForNewLocation', async () => {
const folder1 = process.cwd(); // absolute path because outside of tmpDir
const tmpDir = os.tmpdir();
const tmpInsideDir = path.join(os.tmpdir(), 'inside');
- return createWorkspace([folder1, tmpInsideDir, path.join(tmpInsideDir, 'somefolder')]).then(workspace => {
- const origContent = fs.readFileSync(workspace.configPath.fsPath).toString();
+ const workspace = await createWorkspace([folder1, tmpInsideDir, path.join(tmpInsideDir, 'somefolder')]);
+ const origContent = fs.readFileSync(workspace.configPath.fsPath).toString();
- let origConfigPath = workspace.configPath;
- let workspaceConfigPath = URI.file(path.join(tmpDir, 'inside', 'myworkspace1.code-workspace'));
- let newContent = rewriteWorkspaceFileForNewLocation(origContent, origConfigPath, workspaceConfigPath);
+ let origConfigPath = workspace.configPath;
+ let workspaceConfigPath = URI.file(path.join(tmpDir, 'inside', 'myworkspace1.code-workspace'));
+ let newContent = rewriteWorkspaceFileForNewLocation(origContent, origConfigPath, workspaceConfigPath);
+ let ws = (JSON.parse(newContent) as IStoredWorkspace);
+ assert.equal(ws.folders.length, 3);
+ assertPathEquals((ws.folders[0]).path, folder1); // absolute path because outside of tmpdir
+ assertPathEquals((ws.folders[1]).path, '.');
+ assertPathEquals((ws.folders[2]).path, 'somefolder');
- let ws = JSON.parse(newContent) as IStoredWorkspace;
- assert.equal(ws.folders.length, 3);
- assertPathEquals((ws.folders[0]).path, folder1); // absolute path because outside of tmpdir
- assertPathEquals((ws.folders[1]).path, '.');
- assertPathEquals((ws.folders[2]).path, 'somefolder');
+ origConfigPath = workspaceConfigPath;
+ workspaceConfigPath = URI.file(path.join(tmpDir, 'myworkspace2.code-workspace'));
+ newContent = rewriteWorkspaceFileForNewLocation(newContent, origConfigPath, workspaceConfigPath);
+ ws = (JSON.parse(newContent) as IStoredWorkspace);
+ assert.equal(ws.folders.length, 3);
+ assertPathEquals((ws.folders[0]).path, folder1);
+ assertPathEquals((ws.folders[1]).path, 'inside');
+ assertPathEquals((ws.folders[2]).path, isWindows ? 'inside\\somefolder' : 'inside/somefolder');
- origConfigPath = workspaceConfigPath;
- workspaceConfigPath = URI.file(path.join(tmpDir, 'myworkspace2.code-workspace'));
- newContent = rewriteWorkspaceFileForNewLocation(newContent, origConfigPath, workspaceConfigPath);
+ origConfigPath = workspaceConfigPath;
+ workspaceConfigPath = URI.file(path.join(tmpDir, 'other', 'myworkspace2.code-workspace'));
+ newContent = rewriteWorkspaceFileForNewLocation(newContent, origConfigPath, workspaceConfigPath);
+ ws = (JSON.parse(newContent) as IStoredWorkspace);
+ assert.equal(ws.folders.length, 3);
+ assertPathEquals((ws.folders[0]).path, folder1);
+ assertPathEquals((ws.folders[1]).path, tmpInsideDir);
+ assertPathEquals((ws.folders[2]).path, path.join(tmpInsideDir, 'somefolder'));
- ws = JSON.parse(newContent) as IStoredWorkspace;
- assert.equal(ws.folders.length, 3);
- assertPathEquals((ws.folders[0]).path, folder1);
- assertPathEquals((ws.folders[1]).path, 'inside');
- assertPathEquals((ws.folders[2]).path, isWindows ? 'inside\\somefolder' : 'inside/somefolder');
+ origConfigPath = workspaceConfigPath;
+ workspaceConfigPath = URI.parse('foo://foo/bar/myworkspace2.code-workspace');
+ newContent = rewriteWorkspaceFileForNewLocation(newContent, origConfigPath, workspaceConfigPath);
+ ws = (JSON.parse(newContent) as IStoredWorkspace);
+ assert.equal(ws.folders.length, 3);
+ assert.equal((ws.folders[0]).uri, URI.file(folder1).toString(true));
+ assert.equal((ws.folders[1]).uri, URI.file(tmpInsideDir).toString(true));
+ assert.equal((ws.folders[2]).uri, URI.file(path.join(tmpInsideDir, 'somefolder')).toString(true));
- origConfigPath = workspaceConfigPath;
- workspaceConfigPath = URI.file(path.join(tmpDir, 'other', 'myworkspace2.code-workspace'));
- newContent = rewriteWorkspaceFileForNewLocation(newContent, origConfigPath, workspaceConfigPath);
-
- ws = JSON.parse(newContent) as IStoredWorkspace;
- assert.equal(ws.folders.length, 3);
- assertPathEquals((ws.folders[0]).path, folder1);
- assertPathEquals((ws.folders[1]).path, tmpInsideDir);
- assertPathEquals((ws.folders[2]).path, path.join(tmpInsideDir, 'somefolder'));
-
- origConfigPath = workspaceConfigPath;
- workspaceConfigPath = URI.parse('foo://foo/bar/myworkspace2.code-workspace');
- newContent = rewriteWorkspaceFileForNewLocation(newContent, origConfigPath, workspaceConfigPath);
-
- ws = JSON.parse(newContent) as IStoredWorkspace;
- assert.equal(ws.folders.length, 3);
- assert.equal((ws.folders[0]).uri, URI.file(folder1).toString(true));
- assert.equal((ws.folders[1]).uri, URI.file(tmpInsideDir).toString(true));
- assert.equal((ws.folders[2]).uri, URI.file(path.join(tmpInsideDir, 'somefolder')).toString(true));
-
- service.deleteUntitledWorkspaceSync(workspace);
- });
+ service.deleteUntitledWorkspaceSync(workspace);
});
- test('rewriteWorkspaceFileForNewLocation (preserves comments)', () => {
- return createWorkspace([process.cwd(), os.tmpdir(), path.join(os.tmpdir(), 'somefolder')]).then(workspace => {
- const workspaceConfigPath = URI.file(path.join(os.tmpdir(), `myworkspace.${Date.now()}.${WORKSPACE_EXTENSION}`));
+ test('rewriteWorkspaceFileForNewLocation (preserves comments)', async () => {
+ const workspace = await createWorkspace([process.cwd(), os.tmpdir(), path.join(os.tmpdir(), 'somefolder')]);
+ const workspaceConfigPath = URI.file(path.join(os.tmpdir(), `myworkspace.${Date.now()}.${WORKSPACE_EXTENSION}`));
- let origContent = fs.readFileSync(workspace.configPath.fsPath).toString();
- origContent = `// this is a comment\n${origContent}`;
+ let origContent = fs.readFileSync(workspace.configPath.fsPath).toString();
+ origContent = `// this is a comment\n${origContent}`;
- let newContent = rewriteWorkspaceFileForNewLocation(origContent, workspace.configPath, workspaceConfigPath);
-
- assert.equal(0, newContent.indexOf('// this is a comment'));
-
- service.deleteUntitledWorkspaceSync(workspace);
- });
+ let newContent = rewriteWorkspaceFileForNewLocation(origContent, workspace.configPath, workspaceConfigPath);
+ assert.equal(0, newContent.indexOf('// this is a comment'));
+ service.deleteUntitledWorkspaceSync(workspace);
});
- test('rewriteWorkspaceFileForNewLocation (preserves forward slashes)', () => {
- return createWorkspace([process.cwd(), os.tmpdir(), path.join(os.tmpdir(), 'somefolder')]).then(workspace => {
- const workspaceConfigPath = URI.file(path.join(os.tmpdir(), `myworkspace.${Date.now()}.${WORKSPACE_EXTENSION}`));
- let origContent = fs.readFileSync(workspace.configPath.fsPath).toString();
- origContent = origContent.replace(/[\\]/g, '/'); // convert backslash to slash
+ test('rewriteWorkspaceFileForNewLocation (preserves forward slashes)', async () => {
+ const workspace = await createWorkspace([process.cwd(), os.tmpdir(), path.join(os.tmpdir(), 'somefolder')]);
+ const workspaceConfigPath = URI.file(path.join(os.tmpdir(), `myworkspace.${Date.now()}.${WORKSPACE_EXTENSION}`));
- const newContent = rewriteWorkspaceFileForNewLocation(origContent, workspace.configPath, workspaceConfigPath);
+ let origContent = fs.readFileSync(workspace.configPath.fsPath).toString();
+ origContent = origContent.replace(/[\\]/g, '/'); // convert backslash to slash
- const ws = JSON.parse(newContent) as IStoredWorkspace;
- assert.ok(ws.folders.every(f => (f).path.indexOf('\\') < 0));
-
- service.deleteUntitledWorkspaceSync(workspace);
- });
+ const newContent = rewriteWorkspaceFileForNewLocation(origContent, workspace.configPath, workspaceConfigPath);
+ const ws = (JSON.parse(newContent) as IStoredWorkspace);
+ assert.ok(ws.folders.every(f => (f).path.indexOf('\\') < 0));
+ service.deleteUntitledWorkspaceSync(workspace);
});
- test('rewriteWorkspaceFileForNewLocation (unc paths)', () => {
+ test('rewriteWorkspaceFileForNewLocation (unc paths)', async () => {
if (!isWindows) {
return Promise.resolve();
}
@@ -326,68 +305,58 @@ suite('WorkspacesMainService', () => {
const folder2Location = '\\\\server\\share2\\some\\path';
const folder3Location = path.join(os.tmpdir(), 'wsloc', 'inner', 'more');
- return createWorkspace([folder1Location, folder2Location, folder3Location]).then(workspace => {
- const workspaceConfigPath = URI.file(path.join(workspaceLocation, `myworkspace.${Date.now()}.${WORKSPACE_EXTENSION}`));
- let origContent = fs.readFileSync(workspace.configPath.fsPath).toString();
+ const workspace = await createWorkspace([folder1Location, folder2Location, folder3Location]);
+ const workspaceConfigPath = URI.file(path.join(workspaceLocation, `myworkspace.${Date.now()}.${WORKSPACE_EXTENSION}`));
+ let origContent = fs.readFileSync(workspace.configPath.fsPath).toString();
+ const newContent = rewriteWorkspaceFileForNewLocation(origContent, workspace.configPath, workspaceConfigPath);
+ const ws = (JSON.parse(newContent) as IStoredWorkspace);
+ assertPathEquals((ws.folders[0]).path, folder1Location);
+ assertPathEquals((ws.folders[1]).path, folder2Location);
+ assertPathEquals((ws.folders[2]).path, 'inner\\more');
- const newContent = rewriteWorkspaceFileForNewLocation(origContent, workspace.configPath, workspaceConfigPath);
-
- const ws = JSON.parse(newContent) as IStoredWorkspace;
- assertPathEquals((ws.folders[0]).path, folder1Location);
- assertPathEquals((ws.folders[1]).path, folder2Location);
- assertPathEquals((ws.folders[2]).path, 'inner\\more');
-
- service.deleteUntitledWorkspaceSync(workspace);
- });
+ service.deleteUntitledWorkspaceSync(workspace);
});
- test('deleteUntitledWorkspaceSync (untitled)', () => {
- return createWorkspace([process.cwd(), os.tmpdir()]).then(workspace => {
- assert.ok(fs.existsSync(workspace.configPath.fsPath));
-
- service.deleteUntitledWorkspaceSync(workspace);
-
- assert.ok(!fs.existsSync(workspace.configPath.fsPath));
- });
+ test('deleteUntitledWorkspaceSync (untitled)', async () => {
+ const workspace = await createWorkspace([process.cwd(), os.tmpdir()]);
+ assert.ok(fs.existsSync(workspace.configPath.fsPath));
+ service.deleteUntitledWorkspaceSync(workspace);
+ assert.ok(!fs.existsSync(workspace.configPath.fsPath));
});
- test('deleteUntitledWorkspaceSync (saved)', () => {
- return createWorkspace([process.cwd(), os.tmpdir()]).then(workspace => {
- service.deleteUntitledWorkspaceSync(workspace);
- });
+ test('deleteUntitledWorkspaceSync (saved)', async () => {
+ const workspace = await createWorkspace([process.cwd(), os.tmpdir()]);
+ service.deleteUntitledWorkspaceSync(workspace);
});
- test('getUntitledWorkspaceSync', () => {
+ test('getUntitledWorkspaceSync', async () => {
let untitled = service.getUntitledWorkspacesSync();
assert.equal(untitled.length, 0);
- return createWorkspace([process.cwd(), os.tmpdir()]).then(untitledOne => {
- assert.ok(fs.existsSync(untitledOne.configPath.fsPath));
+ const untitledOne = await createWorkspace([process.cwd(), os.tmpdir()]);
+ assert.ok(fs.existsSync(untitledOne.configPath.fsPath));
- untitled = service.getUntitledWorkspacesSync();
+ untitled = service.getUntitledWorkspacesSync();
+ assert.equal(1, untitled.length);
+ assert.equal(untitledOne.id, untitled[0].workspace.id);
- assert.equal(1, untitled.length);
- assert.equal(untitledOne.id, untitled[0].workspace.id);
+ const untitledTwo = await createWorkspace([os.tmpdir(), process.cwd()]);
+ assert.ok(fs.existsSync(untitledTwo.configPath.fsPath));
+ assert.ok(fs.existsSync(untitledOne.configPath.fsPath), `Unexpected workspaces count of 1 (expected 2): ${untitledOne.configPath.fsPath} does not exist anymore?`);
+ untitled = service.getUntitledWorkspacesSync();
+ assert.ok(fs.existsSync(untitledOne.configPath.fsPath), `Unexpected workspaces count of 1 (expected 2): ${untitledOne.configPath.fsPath} does not exist anymore?`);
+ if (untitled.length === 1) {
+ const untitledHome = dirname(dirname(untitledTwo.configPath));
+ assert.fail(`Unexpected workspaces count of 1 (expected 2), all workspaces:\n ${fs.readdirSync(untitledHome.fsPath).map(name => fs.readFileSync(joinPath(untitledHome, name, 'workspace.json').fsPath, 'utf8'))}`);
+ }
+ assert.equal(2, untitled.length);
- return createWorkspace([os.tmpdir(), process.cwd()]).then(untitledTwo => {
- assert.ok(fs.existsSync(untitledTwo.configPath.fsPath));
+ service.deleteUntitledWorkspaceSync(untitledOne);
+ untitled = service.getUntitledWorkspacesSync();
+ assert.equal(1, untitled.length);
- untitled = service.getUntitledWorkspacesSync();
-
- if (untitled.length === 1) {
- assert.fail('Unexpected workspaces count, contents:\n' + fs.readFileSync(untitledTwo.configPath.fsPath, 'utf8'));
- }
-
- assert.equal(2, untitled.length);
-
- service.deleteUntitledWorkspaceSync(untitledOne);
- untitled = service.getUntitledWorkspacesSync();
- assert.equal(1, untitled.length);
-
- service.deleteUntitledWorkspaceSync(untitledTwo);
- untitled = service.getUntitledWorkspacesSync();
- assert.equal(0, untitled.length);
- });
- });
+ service.deleteUntitledWorkspaceSync(untitledTwo);
+ untitled = service.getUntitledWorkspacesSync();
+ assert.equal(0, untitled.length);
});
});
diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts
index 35796b7251..21cf23818c 100644
--- a/src/vs/vscode.proposed.d.ts
+++ b/src/vs/vscode.proposed.d.ts
@@ -565,22 +565,6 @@ declare module 'vscode' {
//#endregion
- //#region Joh: onDidExecuteCommand
-
- export interface CommandExecutionEvent {
- command: string;
- arguments: any[];
- }
-
- export namespace commands {
- /**
- * An event that is emitted when a [command](#Command) is executed.
- */
- export const onDidExecuteCommand: Event;
- }
-
- //#endregion
-
//#region Joh: decorations
//todo@joh -> make class
@@ -1046,6 +1030,15 @@ declare module 'vscode' {
*/
constructor(label: TreeItemLabel, collapsibleState?: TreeItemCollapsibleState);
}
+
+ export interface TreeViewOptions2 extends TreeViewOptions {
+ /**
+ * Whether the tree supports multi-select. When the tree supports multi-select and a command is executed from the tree,
+ * the first argument to the command is the tree item that the command was executed on and the second argument is an
+ * array containing the other selected tree items.
+ */
+ canSelectMany?: boolean;
+ }
//#endregion
//#region CustomExecution
@@ -1168,11 +1161,11 @@ declare module 'vscode' {
/**
* Content security policy source for webview resources.
*
- * This is origin used in a content security policy rule:
+ * This is the origin that should be used in a content security policy rule:
*
* ```
* img-src https: ${webview.cspSource} ...;
- * ````
+ * ```
*/
readonly cspSource: string;
}
diff --git a/src/vs/workbench/api/browser/mainThreadCodeInsets.ts b/src/vs/workbench/api/browser/mainThreadCodeInsets.ts
index ac02f14f9a..f0d144d58a 100644
--- a/src/vs/workbench/api/browser/mainThreadCodeInsets.ts
+++ b/src/vs/workbench/api/browser/mainThreadCodeInsets.ts
@@ -90,7 +90,6 @@ export class MainThreadEditorInsets implements MainThreadEditorInsetsShape {
const webview = this._webviewService.createWebview('' + handle, {
enableFindWidget: false,
- allowSvgs: false,
extension: { id: extensionId, location: URI.revive(extensionLocation) }
}, {
allowScripts: options.enableScripts,
diff --git a/src/vs/workbench/api/browser/mainThreadCommands.ts b/src/vs/workbench/api/browser/mainThreadCommands.ts
index 81aa57e579..e6e86b909f 100644
--- a/src/vs/workbench/api/browser/mainThreadCommands.ts
+++ b/src/vs/workbench/api/browser/mainThreadCommands.ts
@@ -15,7 +15,6 @@ export class MainThreadCommands implements MainThreadCommandsShape {
private readonly _commandRegistrations = new Map();
private readonly _generateCommandsDocumentationRegistration: IDisposable;
private readonly _proxy: ExtHostCommandsShape;
- private _onDidExecuteCommandListener?: IDisposable;
constructor(
extHostContext: IExtHostContext,
@@ -78,19 +77,6 @@ export class MainThreadCommands implements MainThreadCommandsShape {
return this._commandService.executeCommand(id, ...args);
}
- $registerCommandListener() {
- if (!this._onDidExecuteCommandListener) {
- this._onDidExecuteCommandListener = this._commandService.onDidExecuteCommand(command => this._proxy.$handleDidExecuteCommand(command));
- }
- }
-
- $unregisterCommandListener() {
- if (this._onDidExecuteCommandListener) {
- this._onDidExecuteCommandListener.dispose();
- this._onDidExecuteCommandListener = undefined;
- }
- }
-
$getCommands(): Promise {
return Promise.resolve([...CommandsRegistry.getCommands().keys()]);
}
diff --git a/src/vs/workbench/api/browser/mainThreadKeytar.ts b/src/vs/workbench/api/browser/mainThreadKeytar.ts
index 0cd602a1db..1f6c748ab2 100644
--- a/src/vs/workbench/api/browser/mainThreadKeytar.ts
+++ b/src/vs/workbench/api/browser/mainThreadKeytar.ts
@@ -5,49 +5,33 @@
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { MainContext, MainThreadKeytarShape, IExtHostContext } from 'vs/workbench/api/common/extHost.protocol';
-import { ICredentialsService } from 'vs/platform/credentials/common/credentials';
-import { optional } from 'vs/platform/instantiation/common/instantiation';
+import { ICredentialsService } from 'vs/workbench/services/credentials/common/credentials';
@extHostNamedCustomer(MainContext.MainThreadKeytar)
export class MainThreadKeytar implements MainThreadKeytarShape {
- private readonly _credentialsService?: ICredentialsService;
-
constructor(
_extHostContext: IExtHostContext,
- @optional(ICredentialsService) credentialsService: ICredentialsService,
- ) {
- this._credentialsService = credentialsService;
+ @ICredentialsService private readonly _credentialsService: ICredentialsService,
+ ) { }
+
+ async $getPassword(service: string, account: string): Promise {
+ return this._credentialsService.getPassword(service, account);
+ }
+
+ async $setPassword(service: string, account: string, password: string): Promise {
+ return this._credentialsService.setPassword(service, account, password);
+ }
+
+ async $deletePassword(service: string, account: string): Promise {
+ return this._credentialsService.deletePassword(service, account);
+ }
+
+ async $findPassword(service: string): Promise {
+ return this._credentialsService.findPassword(service);
}
dispose(): void {
//
}
-
- async $getPassword(service: string, account: string): Promise {
- if (this._credentialsService) {
- return this._credentialsService.getPassword(service, account);
- }
- return null;
- }
-
- async $setPassword(service: string, account: string, password: string): Promise {
- if (this._credentialsService) {
- return this._credentialsService.setPassword(service, account, password);
- }
- }
-
- async $deletePassword(service: string, account: string): Promise {
- if (this._credentialsService) {
- return this._credentialsService.deletePassword(service, account);
- }
- return false;
- }
-
- async $findPassword(service: string): Promise {
- if (this._credentialsService) {
- return this._credentialsService.findPassword(service);
- }
- return null;
- }
}
diff --git a/src/vs/workbench/api/browser/mainThreadTreeViews.ts b/src/vs/workbench/api/browser/mainThreadTreeViews.ts
index 0337069629..e9ac823fa7 100644
--- a/src/vs/workbench/api/browser/mainThreadTreeViews.ts
+++ b/src/vs/workbench/api/browser/mainThreadTreeViews.ts
@@ -27,13 +27,14 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTreeViews);
}
- $registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean }): void {
+ $registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean, canSelectMany: boolean }): void {
const dataProvider = new TreeViewDataProvider(treeViewId, this._proxy, this.notificationService);
this._dataProviders.set(treeViewId, dataProvider);
const viewer = this.getTreeView(treeViewId);
if (viewer) {
viewer.dataProvider = dataProvider;
viewer.showCollapseAllAction = !!options.showCollapseAll;
+ viewer.canSelectMany = !!options.canSelectMany;
this.registerListeners(treeViewId, viewer);
this._proxy.$setVisible(treeViewId, viewer.visible);
} else {
diff --git a/src/vs/workbench/api/browser/mainThreadWebview.ts b/src/vs/workbench/api/browser/mainThreadWebview.ts
index c718af569e..d181d029dd 100644
--- a/src/vs/workbench/api/browser/mainThreadWebview.ts
+++ b/src/vs/workbench/api/browser/mainThreadWebview.ts
@@ -327,7 +327,7 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
if (MainThreadWebviews.standardSupportedLinkSchemes.has(link.scheme)) {
return true;
}
- if (this._productService.productConfiguration.urlProtocol === link.scheme) {
+ if (this._productService.urlProtocol === link.scheme) {
return true;
}
return !!webview.webview.contentOptions.enableCommandUris && link.scheme === 'command';
diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts
index 077a47ea29..b28522521d 100644
--- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts
+++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts
@@ -151,7 +151,7 @@ const viewsContribution: IJSONSchema = {
default: []
},
'remote': {
- description: localize('views.remote', "Contributes views to Remote container in the Activity bar"),
+ description: localize('views.remote', "Contributes views to Remote container in the Activity bar. To contribute to this container, enableProposedApi needs to be turned on"),
type: 'array',
items: nestableViewDescriptor,
default: []
@@ -387,6 +387,11 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
return;
}
+ if (entry.key === 'remote' && !extension.description.enableProposedApi) {
+ collector.warn(localize('ViewContainerRequiresProposedAPI', "View container '{0}' requires 'enableProposedApi' turned on to be added to 'Remote'.", entry.key));
+ return;
+ }
+
const viewContainer = this.getViewContainer(entry.key);
if (!viewContainer) {
collector.warn(localize('ViewContainerDoesnotExist', "View container '{0}' does not exist and all views registered to it will be added to 'Explorer'.", entry.key));
diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts
index 588563d01f..fd09c640e5 100644
--- a/src/vs/workbench/api/common/extHost.api.impl.ts
+++ b/src/vs/workbench/api/common/extHost.api.impl.ts
@@ -225,11 +225,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
},
getCommands(filterInternal: boolean = false): Thenable {
return extHostCommands.getCommands(filterInternal);
- },
- onDidExecuteCommand: proposedApiFunction(extension, (listener, thisArgs?, disposables?) => {
- checkProposedApiEnabled(extension);
- return extHostCommands.onDidExecuteCommand(listener, thisArgs, disposables);
- }),
+ }
};
// namespace: env
diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts
index de80ece9ae..35f3d0a695 100644
--- a/src/vs/workbench/api/common/extHost.protocol.ts
+++ b/src/vs/workbench/api/common/extHost.protocol.ts
@@ -21,7 +21,7 @@ import { EndOfLineSequence, ISingleEditOperation } from 'vs/editor/common/model'
import { IModelChangedEvent } from 'vs/editor/common/model/mirrorTextModel';
import * as modes from 'vs/editor/common/modes';
import { CharacterPair, CommentRule, EnterAction } from 'vs/editor/common/modes/languageConfiguration';
-import { ICommandHandlerDescription, ICommandEvent } from 'vs/platform/commands/common/commands';
+import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands';
import { ConfigurationTarget, IConfigurationData, IConfigurationModel } from 'vs/platform/configuration/common/configuration';
import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
@@ -119,8 +119,6 @@ export interface MainThreadClipboardShape extends IDisposable {
export interface MainThreadCommandsShape extends IDisposable {
$registerCommand(id: string): void;
- $registerCommandListener(): void;
- $unregisterCommandListener(): void;
$unregisterCommand(id: string): void;
$executeCommand(id: string, args: any[]): Promise;
$getCommands(): Promise;
@@ -246,7 +244,7 @@ export interface MainThreadTextEditorsShape extends IDisposable {
}
export interface MainThreadTreeViewsShape extends IDisposable {
- $registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean }): void;
+ $registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean, canSelectMany: boolean }): void;
$refresh(treeViewId: string, itemsToRefresh?: { [treeItemHandle: string]: ITreeItem }): Promise;
$reveal(treeViewId: string, treeItem: ITreeItem, parentChain: ITreeItem[], options: IRevealOptions): Promise;
$setMessage(treeViewId: string, message: string): void;
@@ -739,7 +737,6 @@ export interface MainThreadWindowShape extends IDisposable {
export interface ExtHostCommandsShape {
$executeContributedCommand(id: string, ...args: any[]): Promise;
$getContributedCommandHandlerDescriptions(): Promise<{ [id: string]: string | ICommandHandlerDescription }>;
- $handleDidExecuteCommand(command: ICommandEvent): void;
}
export interface ExtHostConfigurationShape {
diff --git a/src/vs/workbench/api/common/extHostCommands.ts b/src/vs/workbench/api/common/extHostCommands.ts
index 7db7439f55..d2d42b1b80 100644
--- a/src/vs/workbench/api/common/extHostCommands.ts
+++ b/src/vs/workbench/api/common/extHostCommands.ts
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { validateConstraint } from 'vs/base/common/types';
-import { ICommandHandlerDescription, ICommandEvent } from 'vs/platform/commands/common/commands';
+import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands';
import * as extHostTypes from 'vs/workbench/api/common/extHostTypes';
import * as extHostTypeConverter from 'vs/workbench/api/common/extHostTypeConverters';
import { cloneAndChange } from 'vs/base/common/objects';
@@ -17,7 +17,6 @@ import { revive } from 'vs/base/common/marshalling';
import { Range } from 'vs/editor/common/core/range';
import { Position } from 'vs/editor/common/core/position';
import { URI } from 'vs/base/common/uri';
-import { Event, Emitter } from 'vs/base/common/event';
import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
@@ -36,9 +35,6 @@ export class ExtHostCommands implements ExtHostCommandsShape {
readonly _serviceBrand: any;
- private readonly _onDidExecuteCommand: Emitter;
- readonly onDidExecuteCommand: Event;
-
private readonly _commands = new Map();
private readonly _proxy: MainThreadCommandsShape;
private readonly _converter: CommandsConverter;
@@ -50,11 +46,6 @@ export class ExtHostCommands implements ExtHostCommandsShape {
@ILogService logService: ILogService
) {
this._proxy = extHostRpc.getProxy(MainContext.MainThreadCommands);
- this._onDidExecuteCommand = new Emitter({
- onFirstListenerDidAdd: () => this._proxy.$registerCommandListener(),
- onLastListenerRemove: () => this._proxy.$unregisterCommandListener(),
- });
- this.onDidExecuteCommand = Event.filter(this._onDidExecuteCommand.event, e => e.command[0] !== '_'); // filter 'private' commands
this._logService = logService;
this._converter = new CommandsConverter(this);
this._argumentProcessors = [
@@ -119,22 +110,13 @@ export class ExtHostCommands implements ExtHostCommandsShape {
});
}
- $handleDidExecuteCommand(command: ICommandEvent): void {
- this._onDidExecuteCommand.fire({
- command: command.commandId,
- arguments: command.args.map(arg => this._argumentProcessors.reduce((r, p) => p.processArgument(r), arg))
- });
- }
-
executeCommand(id: string, ...args: any[]): Promise {
this._logService.trace('ExtHostCommands#executeCommand', id);
if (this._commands.has(id)) {
// we stay inside the extension host and support
// to pass any kind of parameters around
- const res = this._executeContributedCommand(id, args);
- this._onDidExecuteCommand.fire({ command: id, arguments: args });
- return res;
+ return this._executeContributedCommand(id, args);
} else {
// automagically convert some argument types
diff --git a/src/vs/workbench/api/node/extHostRequireInterceptor.ts b/src/vs/workbench/api/common/extHostRequireInterceptor.ts
similarity index 60%
rename from src/vs/workbench/api/node/extHostRequireInterceptor.ts
rename to src/vs/workbench/api/common/extHostRequireInterceptor.ts
index 4355080351..1d2f89ffc7 100644
--- a/src/vs/workbench/api/node/extHostRequireInterceptor.ts
+++ b/src/vs/workbench/api/common/extHostRequireInterceptor.ts
@@ -5,14 +5,22 @@
import { TernarySearchTree } from 'vs/base/common/map';
import { URI } from 'vs/base/common/uri';
-import { MainThreadKeytarShape, IEnvironment, MainThreadWindowShape, MainThreadTelemetryShape } from 'vs/workbench/api/common/extHost.protocol';
-import { ExtHostConfigProvider } from 'vs/workbench/api/common/extHostConfiguration';
+import { MainThreadTelemetryShape, MainContext } from 'vs/workbench/api/common/extHost.protocol';
+import { ExtHostConfigProvider, IExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration';
import { nullExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
import * as vscode from 'vscode';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { endsWith } from 'vs/base/common/strings';
import { IExtensionApiFactory } from 'vs/workbench/api/common/extHost.api.impl';
+import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
+import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
+import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
+import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService';
+import { platform } from 'vs/base/common/process';
+
+import { AzdataNodeModuleFactory, SqlopsNodeModuleFactory } from 'sql/workbench/api/common/extHostRequireInterceptor'; // {{SQL CARBON EDIT}}
+import { IExtensionApiFactory as sqlIApiFactory } from 'sql/workbench/api/common/sqlExtHost.api.impl'; // {{SQL CARBON EDIT}}
interface LoadFunction {
@@ -21,40 +29,45 @@ interface LoadFunction {
export interface INodeModuleFactory { //{{SQL CARBON EDIT}} export interface
readonly nodeModuleName: string | string[];
- load(request: string, parent: { filename: string; }, isMain: any, original: LoadFunction): any;
- alternaiveModuleName?(name: string): string | undefined;
+ load(request: string, parent: URI, isMain: any, original: LoadFunction): any;
+ alternativeModuleName?(name: string): string | undefined;
}
-export class NodeModuleRequireInterceptor {
- public static INSTANCE = new NodeModuleRequireInterceptor();
+export abstract class RequireInterceptor {
- private readonly _factories: Map;
- private readonly _alternatives: ((moduleName: string) => string | undefined)[];
+ protected readonly _factories: Map;
+ protected readonly _alternatives: ((moduleName: string) => string | undefined)[];
- constructor() {
+ constructor(
+ private _apiFactory: sqlIApiFactory, // {{SQL CARBON EDIT}} replace with ours
+ private _extensionRegistry: ExtensionDescriptionRegistry,
+ @IInstantiationService private readonly _instaService: IInstantiationService,
+ @IExtHostConfiguration private readonly _extHostConfiguration: IExtHostConfiguration,
+ @IExtHostExtensionService private readonly _extHostExtensionService: IExtHostExtensionService,
+ @IExtHostInitDataService private readonly _initData: IExtHostInitDataService
+ ) {
this._factories = new Map();
this._alternatives = [];
- this._installInterceptor(this._factories, this._alternatives);
}
- private _installInterceptor(factories: Map, alternatives: ((moduleName: string) => string | undefined)[]): void {
- const node_module = require.__$__nodeRequire('module');
- const original = node_module._load;
- node_module._load = function load(request: string, parent: { filename: string; }, isMain: any) {
- for (let alternativeModuleName of alternatives) {
- let alternative = alternativeModuleName(request);
- if (alternative) {
- request = alternative;
- break;
- }
- }
- if (!factories.has(request)) {
- return original.apply(this, arguments);
- }
- return factories.get(request)!.load(request, parent, isMain, original);
- };
+ async install(): Promise {
+
+ this._installInterceptor();
+
+ const configProvider = await this._extHostConfiguration.getConfigProvider();
+ const extensionPaths = await this._extHostExtensionService.getExtensionPathIndex();
+
+ this.register(new VSCodeNodeModuleFactory(this._apiFactory.vscode, extensionPaths, this._extensionRegistry, configProvider)); // {{SQL CARBON EDIT}} // add node module
+ this.register(new AzdataNodeModuleFactory(this._apiFactory.azdata, extensionPaths)); // {{SQL CARBON EDIT}} // add node module
+ this.register(new SqlopsNodeModuleFactory(this._apiFactory.sqlops, extensionPaths)); // {{SQL CARBON EDIT}} // add node module
+ this.register(this._instaService.createInstance(KeytarNodeModuleFactory));
+ if (this._initData.remote.isRemote) {
+ this.register(this._instaService.createInstance(OpenNodeModuleFactory, extensionPaths));
+ }
}
+ protected abstract _installInterceptor(): void;
+
public register(interceptor: INodeModuleFactory): void {
if (Array.isArray(interceptor.nodeModuleName)) {
for (let moduleName of interceptor.nodeModuleName) {
@@ -63,15 +76,17 @@ export class NodeModuleRequireInterceptor {
} else {
this._factories.set(interceptor.nodeModuleName, interceptor);
}
- if (typeof interceptor.alternaiveModuleName === 'function') {
+ if (typeof interceptor.alternativeModuleName === 'function') {
this._alternatives.push((moduleName) => {
- return interceptor.alternaiveModuleName!(moduleName);
+ return interceptor.alternativeModuleName!(moduleName);
});
}
}
}
-export class VSCodeNodeModuleFactory implements INodeModuleFactory {
+//#region --- vscode-module
+
+class VSCodeNodeModuleFactory implements INodeModuleFactory {
public readonly nodeModuleName = 'vscode';
private readonly _extApiImpl = new Map();
@@ -85,10 +100,10 @@ export class VSCodeNodeModuleFactory implements INodeModuleFactory {
) {
}
- public load(request: string, parent: { filename: string; }): any {
+ public load(_request: string, parent: URI): any {
// get extension id from filename and api for extension
- const ext = this._extensionPaths.findSubstr(URI.file(parent.filename).fsPath);
+ const ext = this._extensionPaths.findSubstr(parent.fsPath);
if (ext) {
let apiImpl = this._extApiImpl.get(ExtensionIdentifier.toKey(ext.identifier));
if (!apiImpl) {
@@ -102,13 +117,18 @@ export class VSCodeNodeModuleFactory implements INodeModuleFactory {
if (!this._defaultApiImpl) {
let extensionPathsPretty = '';
this._extensionPaths.forEach((value, index) => extensionPathsPretty += `\t${index} -> ${value.identifier.value}\n`);
- console.warn(`Could not identify extension for 'vscode' require call from ${parent.filename}. These are the extension path mappings: \n${extensionPathsPretty}`);
+ console.warn(`Could not identify extension for 'vscode' require call from ${parent.fsPath}. These are the extension path mappings: \n${extensionPathsPretty}`);
this._defaultApiImpl = this._apiFactory(nullExtensionDescription, this._extensionRegistry, this._configProvider);
}
return this._defaultApiImpl;
}
}
+//#endregion
+
+
+//#region --- keytar-module
+
interface IKeytarModule {
getPassword(service: string, account: string): Promise;
setPassword(service: string, account: string, password: string): Promise;
@@ -116,16 +136,23 @@ interface IKeytarModule {
findPassword(service: string): Promise;
}
-export class KeytarNodeModuleFactory implements INodeModuleFactory {
+class KeytarNodeModuleFactory implements INodeModuleFactory {
public readonly nodeModuleName: string = 'keytar';
private alternativeNames: Set | undefined;
private _impl: IKeytarModule;
- constructor(mainThreadKeytar: MainThreadKeytarShape, environment: IEnvironment) {
+ constructor(
+ @IExtHostRpcService rpcService: IExtHostRpcService,
+ @IExtHostInitDataService initData: IExtHostInitDataService,
+
+ ) {
+ const { environment } = initData;
+ const mainThreadKeytar = rpcService.getProxy(MainContext.MainThreadKeytar);
+
if (environment.appRoot) {
let appRoot = environment.appRoot.fsPath;
- if (process.platform === 'win32') {
+ if (platform === 'win32') {
appRoot = appRoot.replace(/\\/g, '/');
}
if (appRoot[appRoot.length - 1] === '/') {
@@ -151,11 +178,11 @@ export class KeytarNodeModuleFactory implements INodeModuleFactory {
};
}
- public load(request: string, parent: { filename: string; }): any {
+ public load(_request: string, _parent: URI): any {
return this._impl;
}
- public alternaiveModuleName(name: string): string | undefined {
+ public alternativeModuleName(name: string): string | undefined {
const length = name.length;
// We need at least something like: `?/keytar` which requires
// more than 7 characters.
@@ -173,6 +200,11 @@ export class KeytarNodeModuleFactory implements INodeModuleFactory {
}
}
+//#endregion
+
+
+//#region --- opn/open-module
+
interface OpenOptions {
wait: boolean;
app: string | string[];
@@ -186,15 +218,23 @@ interface IOpenModule {
(target: string, options?: OpenOptions): Thenable;
}
-export class OpenNodeModuleFactory implements INodeModuleFactory {
+class OpenNodeModuleFactory implements INodeModuleFactory {
public readonly nodeModuleName: string[] = ['open', 'opn'];
private _extensionId: string | undefined;
private _original?: IOriginalOpen;
private _impl: IOpenModule;
+ private _mainThreadTelemetry: MainThreadTelemetryShape;
+
+ constructor(
+ private readonly _extensionPaths: TernarySearchTree,
+ @IExtHostRpcService rpcService: IExtHostRpcService,
+ ) {
+
+ this._mainThreadTelemetry = rpcService.getProxy(MainContext.MainThreadTelemetry);
+ const mainThreadWindow = rpcService.getProxy(MainContext.MainThreadWindow);
- constructor(mainThreadWindow: MainThreadWindowShape, private _mainThreadTelemerty: MainThreadTelemetryShape, private readonly _extensionPaths: TernarySearchTree) {
this._impl = (target, options) => {
const uri: URI = URI.parse(target);
// If we have options use the original method.
@@ -210,15 +250,15 @@ export class OpenNodeModuleFactory implements INodeModuleFactory {
};
}
- public load(request: string, parent: { filename: string; }, isMain: any, original: LoadFunction): any {
+ public load(request: string, parent: URI, isMain: any, original: LoadFunction): any {
// get extension id from filename and api for extension
- const extension = this._extensionPaths.findSubstr(URI.file(parent.filename).fsPath);
+ const extension = this._extensionPaths.findSubstr(parent.fsPath);
if (extension) {
this._extensionId = extension.identifier.value;
this.sendShimmingTelemetry();
}
- this._original = original(request, parent, isMain);
+ this._original = original(request, { filename: parent.fsPath }, isMain);
return this._impl;
}
@@ -234,7 +274,7 @@ export class OpenNodeModuleFactory implements INodeModuleFactory {
type ShimmingOpenClassification = {
extension: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
};
- this._mainThreadTelemerty.$publicLog2<{ extension: string }, ShimmingOpenClassification>('shimming.open', { extension: this._extensionId });
+ this._mainThreadTelemetry.$publicLog2<{ extension: string }, ShimmingOpenClassification>('shimming.open', { extension: this._extensionId });
}
private sendNoForwardTelemetry(): void {
@@ -244,6 +284,8 @@ export class OpenNodeModuleFactory implements INodeModuleFactory {
type ShimmingOpenCallNoForwardClassification = {
extension: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
};
- this._mainThreadTelemerty.$publicLog2<{ extension: string }, ShimmingOpenCallNoForwardClassification>('shimming.open.call.noForward', { extension: this._extensionId });
+ this._mainThreadTelemetry.$publicLog2<{ extension: string }, ShimmingOpenCallNoForwardClassification>('shimming.open.call.noForward', { extension: this._extensionId });
}
}
+
+//#endregion
diff --git a/src/vs/workbench/api/common/extHostTreeViews.ts b/src/vs/workbench/api/common/extHostTreeViews.ts
index 9dc99da82c..98902993a7 100644
--- a/src/vs/workbench/api/common/extHostTreeViews.ts
+++ b/src/vs/workbench/api/common/extHostTreeViews.ts
@@ -55,10 +55,21 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape {
private commands: ExtHostCommands,
private logService: ILogService
) {
+
+ function isTreeViewItemHandleArg(arg: any): boolean {
+ return arg && arg.$treeViewId && arg.$treeItemHandle;
+ }
commands.registerArgumentProcessor({
processArgument: arg => {
- if (arg && arg.$treeViewId && arg.$treeItemHandle) {
+ if (isTreeViewItemHandleArg(arg)) {
return this.convertArgument(arg);
+ } else if (Array.isArray(arg) && (arg.length > 0)) {
+ return arg.map(item => {
+ if (isTreeViewItemHandleArg(item)) {
+ return this.convertArgument(item);
+ }
+ return item;
+ });
}
return arg;
}
@@ -187,13 +198,10 @@ export class ExtHostTreeView extends Disposable {
private refreshPromise: Promise = Promise.resolve();
private refreshQueue: Promise = Promise.resolve();
- constructor(private viewId: string, options: vscode.TreeViewOptions, private proxy: MainThreadTreeViewsShape, private commands: CommandsConverter, private logService: ILogService, private extension: IExtensionDescription) {
+ constructor(private viewId: string, options: vscode.TreeViewOptions2, private proxy: MainThreadTreeViewsShape, private commands: CommandsConverter, private logService: ILogService, private extension: IExtensionDescription) {
super();
this.dataProvider = options.treeDataProvider;
- // {{SQL CARBON EDIT}}
- if (this.proxy) {
- this.proxy.$registerTreeViewDataProvider(viewId, { showCollapseAll: !!options.showCollapseAll });
- }
+ this.proxy.$registerTreeViewDataProvider(viewId, { showCollapseAll: !!options.showCollapseAll, canSelectMany: !!options.canSelectMany });
if (this.dataProvider.onDidChangeTreeData) {
this._register(this.dataProvider.onDidChangeTreeData(element => this._onDidChangeData.fire({ message: false, element })));
}
diff --git a/src/vs/workbench/api/node/extHostExtensionService.ts b/src/vs/workbench/api/node/extHostExtensionService.ts
index 80d7ccaf1e..f3e9aae4d4 100644
--- a/src/vs/workbench/api/node/extHostExtensionService.ts
+++ b/src/vs/workbench/api/node/extHostExtensionService.ts
@@ -3,8 +3,8 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import { createApiFactoryAndRegisterActors } from 'vs/workbench/api/common/extHost.api.impl';
-import { NodeModuleRequireInterceptor, VSCodeNodeModuleFactory, KeytarNodeModuleFactory, OpenNodeModuleFactory } from 'vs/workbench/api/node/extHostRequireInterceptor';
+import { createApiFactoryAndRegisterActors } from 'sql/workbench/api/common/sqlExtHost.api.impl'; // {{SQL CARBON EDIT}} replace with ours
+import { RequireInterceptor } from 'vs/workbench/api/common/extHostRequireInterceptor';
import { MainContext } from 'vs/workbench/api/common/extHost.protocol';
import { ExtensionActivationTimesBuilder } from 'vs/workbench/api/common/extHostExtensionActivator';
import { connectProxyResolver } from 'vs/workbench/services/extensions/node/proxyResolver';
@@ -14,15 +14,33 @@ import { CLIServer } from 'vs/workbench/api/node/extHostCLIServer';
import { URI } from 'vs/base/common/uri';
import { Schemas } from 'vs/base/common/network';
-import { createAdsApiFactory } from 'sql/workbench/api/common/sqlExtHost.api.impl'; // {{SQL CARBON EDIT}} use our extension initalizer
-import { AzdataNodeModuleFactory, SqlopsNodeModuleFactory } from 'sql/workbench/api/node/extHostRequireInterceptor'; // {{SQL CARBON EDIT}} use our extension initalizer
+class NodeModuleRequireInterceptor extends RequireInterceptor {
+
+ protected _installInterceptor(): void {
+ const that = this;
+ const node_module =